Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions migrations/20260318030206_telemetry_table.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
DROP TABLE IF EXISTS telemetry_interactions;
DROP TABLE IF EXISTS telemetry_completions;
27 changes: 27 additions & 0 deletions migrations/20260318030206_telemetry_table.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
CREATE TABLE IF NOT EXISTS telemetry_interactions (
id BIGSERIAL PRIMARY KEY,
correlation_id VARCHAR(12) NOT NULL,
timestamp TIMESTAMPTZ NOT NULL,
received_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
interaction_type VARCHAR(20) NOT NULL,
user_id BIGINT NOT NULL,
command_name VARCHAR(100),
guild_id BIGINT,
guild_name VARCHAR(100),
channel_id BIGINT NOT NULL,
options JSONB NOT NULL DEFAULT '{}'::jsonb,
bot_version VARCHAR(20) NOT NULL DEFAULT 'unknown'
);

CREATE TABLE IF NOT EXISTS telemetry_completions (
id BIGSERIAL PRIMARY KEY,
correlation_id VARCHAR(12) NOT NULL,
timestamp TIMESTAMPTZ NOT NULL,
received_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
command_name VARCHAR(100) NOT NULL,
status VARCHAR(20) NOT NULL,
duration_ms NUMERIC(10,2),
error_type VARCHAR(100),
CONSTRAINT telemetry_completions_status_check
CHECK (status IN ('success', 'user_error', 'internal_error'))
);
14 changes: 7 additions & 7 deletions tests/benchmarks/benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func BenchmarkGetUser(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
req, _ := http.NewRequest("GET", benchServer.URL+"/v1/users/"+benchUserID, nil)
req, _ := http.NewRequest("GET", benchServer.URL+"/api/v1/users/"+benchUserID, nil)
req.AddCookie(&http.Cookie{Name: "capy_auth", Value: benchJWTToken})
resp, err := benchClient.Do(req)
if err != nil {
Expand All @@ -130,7 +130,7 @@ func BenchmarkListOrganizations(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
req, _ := http.NewRequest("GET", benchServer.URL+"/v1/organizations", nil)
req, _ := http.NewRequest("GET", benchServer.URL+"/api/v1/organizations", nil)
req.AddCookie(&http.Cookie{Name: "capy_auth", Value: benchJWTToken})
resp, err := benchClient.Do(req)
if err != nil {
Expand All @@ -152,7 +152,7 @@ func BenchmarkCreateOrganization(b *testing.B) {
"slug": fmt.Sprintf("bench-org-%d", i),
}
jsonBody, _ := json.Marshal(body)
req, _ := http.NewRequest("POST", benchServer.URL+"/v1/organizations", bytes.NewBuffer(jsonBody))
req, _ := http.NewRequest("POST", benchServer.URL+"/api/v1/organizations", bytes.NewBuffer(jsonBody))
req.AddCookie(&http.Cookie{Name: "capy_auth", Value: benchJWTToken})
req.Header.Set("Content-Type", "application/json")
resp, err := benchClient.Do(req)
Expand All @@ -170,7 +170,7 @@ func BenchmarkGetOrganization(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
req, _ := http.NewRequest("GET", benchServer.URL+"/v1/organizations/"+benchOrgID, nil)
req, _ := http.NewRequest("GET", benchServer.URL+"/api/v1/organizations/"+benchOrgID, nil)
req.AddCookie(&http.Cookie{Name: "capy_auth", Value: benchJWTToken})
resp, err := benchClient.Do(req)
if err != nil {
Expand All @@ -187,7 +187,7 @@ func BenchmarkListEvents(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
req, _ := http.NewRequest("GET", benchServer.URL+"/v1/events", nil)
req, _ := http.NewRequest("GET", benchServer.URL+"/api/v1/events", nil)
req.AddCookie(&http.Cookie{Name: "capy_auth", Value: benchJWTToken})
resp, err := benchClient.Do(req)
if err != nil {
Expand All @@ -210,7 +210,7 @@ func BenchmarkCreateEvent(b *testing.B) {
"org_id": benchOrgID,
}
jsonBody, _ := json.Marshal(body)
req, _ := http.NewRequest("POST", benchServer.URL+"/v1/events", bytes.NewBuffer(jsonBody))
req, _ := http.NewRequest("POST", benchServer.URL+"/api/v1/events", bytes.NewBuffer(jsonBody))
req.AddCookie(&http.Cookie{Name: "capy_auth", Value: benchJWTToken})
req.Header.Set("Content-Type", "application/json")
resp, err := benchClient.Do(req)
Expand All @@ -228,7 +228,7 @@ func BenchmarkGetEvent(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
req, _ := http.NewRequest("GET", benchServer.URL+"/v1/events/"+benchEventID, nil)
req, _ := http.NewRequest("GET", benchServer.URL+"/api/v1/events/"+benchEventID, nil)
req.AddCookie(&http.Cookie{Name: "capy_auth", Value: benchJWTToken})
resp, err := benchClient.Do(req)
if err != nil {
Expand Down
50 changes: 25 additions & 25 deletions tests/integration/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func TestFullAPI(t *testing.T) {

// Verify User is Member of Org
orgID := orgResponse["oid"].(string)
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/v1/organizations/%s/members", server.URL, orgID), nil)
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/api/v1/organizations/%s/members", server.URL, orgID), nil)
req.AddCookie(cookie)

resp, err = client.Do(req)
Expand Down Expand Up @@ -172,15 +172,15 @@ func TestEventFlow(t *testing.T) {
require.NoError(t, err)

// GET event by id
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/v1/events/%s", server.URL, evResp.EID.String()), nil)
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/api/v1/events/%s", server.URL, evResp.EID.String()), nil)
req.AddCookie(cookie)
resp, err = client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()
require.Equal(t, http.StatusOK, resp.StatusCode)

// List events by org
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/v1/events/org/%s", server.URL, oid), nil)
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/api/v1/events/org/%s", server.URL, oid), nil)
req.AddCookie(cookie)
resp, err = client.Do(req)
require.NoError(t, err)
Expand All @@ -193,7 +193,7 @@ func TestEventFlow(t *testing.T) {

// Register user for event
regBody := fmt.Sprintf(`{"uid":"%s","is_attending":true}`, user.Uid.String())
req, _ = http.NewRequest("POST", fmt.Sprintf("%s/v1/events/%s/register", server.URL, evResp.EID.String()), bytes.NewBuffer([]byte(regBody)))
req, _ = http.NewRequest("POST", fmt.Sprintf("%s/api/v1/events/%s/register", server.URL, evResp.EID.String()), bytes.NewBuffer([]byte(regBody)))
req.AddCookie(cookie)
req.Header.Set("Content-Type", "application/json")
resp, err = client.Do(req)
Expand All @@ -202,7 +202,7 @@ func TestEventFlow(t *testing.T) {
require.Equal(t, http.StatusCreated, resp.StatusCode)

// Register without uid -> should return 400
req, _ = http.NewRequest("POST", fmt.Sprintf("%s/v1/events/%s/register", server.URL, evResp.EID.String()), bytes.NewBuffer([]byte(`{"is_attending":true}`)))
req, _ = http.NewRequest("POST", fmt.Sprintf("%s/api/v1/events/%s/register", server.URL, evResp.EID.String()), bytes.NewBuffer([]byte(`{"is_attending":true}`)))
req.AddCookie(cookie)
req.Header.Set("Content-Type", "application/json")
resp, err = client.Do(req)
Expand All @@ -211,7 +211,7 @@ func TestEventFlow(t *testing.T) {
require.Equal(t, http.StatusBadRequest, resp.StatusCode)

// List registrations
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/v1/events/%s/registrations", server.URL, evResp.EID.String()), nil)
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/api/v1/events/%s/registrations", server.URL, evResp.EID.String()), nil)
req.AddCookie(cookie)
resp, err = client.Do(req)
require.NoError(t, err)
Expand All @@ -224,7 +224,7 @@ func TestEventFlow(t *testing.T) {
assert.Equal(t, user.Uid.String(), regs[0].UID.String())

// Verify user events endpoint
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/v1/users/%s/events", server.URL, user.Uid.String()), nil)
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/api/v1/users/%s/events", server.URL, user.Uid.String()), nil)
req.AddCookie(cookie)
resp, err = client.Do(req)
require.NoError(t, err)
Expand All @@ -236,7 +236,7 @@ func TestEventFlow(t *testing.T) {
require.GreaterOrEqual(t, len(uevents), 1)

// List all events
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/v1/events", server.URL), nil)
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/api/v1/events", server.URL), nil)
req.AddCookie(cookie)
resp, err = client.Do(req)
require.NoError(t, err)
Expand Down Expand Up @@ -272,7 +272,7 @@ func TestUserUpdateDelete(t *testing.T) {

// Update user
updateBody := []byte(`{"first_name":"Updated","last_name":"Name"}`)
req, _ := http.NewRequest("PUT", fmt.Sprintf("%s/v1/users/%s", server.URL, user.Uid.String()), bytes.NewBuffer(updateBody))
req, _ := http.NewRequest("PUT", fmt.Sprintf("%s/api/v1/users/%s", server.URL, user.Uid.String()), bytes.NewBuffer(updateBody))
req.AddCookie(cookie)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
Expand All @@ -287,15 +287,15 @@ func TestUserUpdateDelete(t *testing.T) {
assert.Equal(t, "Name", ur.LastName)

// Delete user
req, _ = http.NewRequest("DELETE", fmt.Sprintf("%s/v1/users/%s", server.URL, user.Uid.String()), nil)
req, _ = http.NewRequest("DELETE", fmt.Sprintf("%s/api/v1/users/%s", server.URL, user.Uid.String()), nil)
req.AddCookie(cookie)
resp, err = client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()
require.Equal(t, http.StatusNoContent, resp.StatusCode)

// Verify deletion
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/v1/users/%s", server.URL, user.Uid.String()), nil)
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/api/v1/users/%s", server.URL, user.Uid.String()), nil)
req.AddCookie(cookie)
resp, err = client.Do(req)
require.NoError(t, err)
Expand Down Expand Up @@ -345,7 +345,7 @@ func TestOrgMemberEndpoints(t *testing.T) {

// Add member to org
addBody := fmt.Sprintf(`{"uid":"%s","is_admin":false}`, member.Uid.String())
req, _ = http.NewRequest("POST", fmt.Sprintf("%s/v1/organizations/%s/members", server.URL, oid), bytes.NewBuffer([]byte(addBody)))
req, _ = http.NewRequest("POST", fmt.Sprintf("%s/api/v1/organizations/%s/members", server.URL, oid), bytes.NewBuffer([]byte(addBody)))
req.AddCookie(cookie)
req.Header.Set("Content-Type", "application/json")
resp, err = client.Do(req)
Expand All @@ -354,7 +354,7 @@ func TestOrgMemberEndpoints(t *testing.T) {
require.Equal(t, http.StatusCreated, resp.StatusCode)

// List org members
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/v1/organizations/%s/members", server.URL, oid), nil)
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/api/v1/organizations/%s/members", server.URL, oid), nil)
req.AddCookie(cookie)
resp, err = client.Do(req)
require.NoError(t, err)
Expand All @@ -367,7 +367,7 @@ func TestOrgMemberEndpoints(t *testing.T) {
require.GreaterOrEqual(t, len(members), 2)

// Verify GetUserOrganizations for member
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/v1/users/%s/organizations", server.URL, member.Uid.String()), nil)
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/api/v1/users/%s/organizations", server.URL, member.Uid.String()), nil)
req.AddCookie(cookie)
resp, err = client.Do(req)
require.NoError(t, err)
Expand Down Expand Up @@ -440,7 +440,7 @@ func TestEventUpdateDeleteFlow(t *testing.T) {

// Update event
updBody := []byte(`{"location":"Updated Room","description":"Updated"}`)
req, _ = http.NewRequest("PUT", fmt.Sprintf("%s/v1/events/%s", server.URL, ev.EID.String()), bytes.NewBuffer(updBody))
req, _ = http.NewRequest("PUT", fmt.Sprintf("%s/api/v1/events/%s", server.URL, ev.EID.String()), bytes.NewBuffer(updBody))
req.AddCookie(cookie)
req.Header.Set("Content-Type", "application/json")
resp, err = client.Do(req)
Expand All @@ -453,7 +453,7 @@ func TestEventUpdateDeleteFlow(t *testing.T) {
assert.Equal(t, "Updated Room", *evUpd.Location)

// Unregister (will error if uid not provided) -> ensure delete works when provided
req, _ = http.NewRequest("DELETE", fmt.Sprintf("%s/v1/events/%s/register?uid=%s", server.URL, ev.EID.String(), user.Uid.String()), nil)
req, _ = http.NewRequest("DELETE", fmt.Sprintf("%s/api/v1/events/%s/register?uid=%s", server.URL, ev.EID.String(), user.Uid.String()), nil)
req.AddCookie(cookie)
resp, err = client.Do(req)
require.NoError(t, err)
Expand All @@ -464,7 +464,7 @@ func TestEventUpdateDeleteFlow(t *testing.T) {
}

// Delete event
req, _ = http.NewRequest("DELETE", fmt.Sprintf("%s/v1/events/%s", server.URL, ev.EID.String()), nil)
req, _ = http.NewRequest("DELETE", fmt.Sprintf("%s/api/v1/events/%s", server.URL, ev.EID.String()), nil)
req.AddCookie(cookie)
resp, err = client.Do(req)
require.NoError(t, err)
Expand Down Expand Up @@ -509,7 +509,7 @@ func TestOrganizationCRUD(t *testing.T) {
oid := orgResp["oid"].(string)

// Get organization
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/v1/organizations/%s", server.URL, oid), nil)
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/api/v1/organizations/%s", server.URL, oid), nil)
req.AddCookie(cookie)
resp, err = client.Do(req)
require.NoError(t, err)
Expand All @@ -518,7 +518,7 @@ func TestOrganizationCRUD(t *testing.T) {

// Update organization
upd := []byte(`{"name":"Crud Org Updated"}`)
req, _ = http.NewRequest("PUT", fmt.Sprintf("%s/v1/organizations/%s", server.URL, oid), bytes.NewBuffer(upd))
req, _ = http.NewRequest("PUT", fmt.Sprintf("%s/api/v1/organizations/%s", server.URL, oid), bytes.NewBuffer(upd))
req.AddCookie(cookie)
req.Header.Set("Content-Type", "application/json")
resp, err = client.Do(req)
Expand Down Expand Up @@ -546,7 +546,7 @@ func TestOrganizationCRUD(t *testing.T) {
member, err := q.CreateUser(ctx, database.CreateUserParams{FirstName: "ToRemove", LastName: "Member", Role: database.NullUserRole{UserRole: database.UserRoleStudent, Valid: true}})
require.NoError(t, err)
addBody := fmt.Sprintf(`{"uid":"%s","is_admin":false}`, member.Uid.String())
req, _ = http.NewRequest("POST", fmt.Sprintf("%s/v1/organizations/%s/members", server.URL, oid), bytes.NewBuffer([]byte(addBody)))
req, _ = http.NewRequest("POST", fmt.Sprintf("%s/api/v1/organizations/%s/members", server.URL, oid), bytes.NewBuffer([]byte(addBody)))
req.AddCookie(cookie)
req.Header.Set("Content-Type", "application/json")
resp, err = client.Do(req)
Expand All @@ -555,15 +555,15 @@ func TestOrganizationCRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, resp.StatusCode)

// Remove member
req, _ = http.NewRequest("DELETE", fmt.Sprintf("%s/v1/organizations/%s/members/%s", server.URL, oid, member.Uid.String()), nil)
req, _ = http.NewRequest("DELETE", fmt.Sprintf("%s/api/v1/organizations/%s/members/%s", server.URL, oid, member.Uid.String()), nil)
req.AddCookie(cookie)
resp, err = client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()
require.Equal(t, http.StatusNoContent, resp.StatusCode)

// List org events (should be empty)
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/v1/organizations/%s/events", server.URL, oid), nil)
req, _ = http.NewRequest("GET", fmt.Sprintf("%s/api/v1/organizations/%s/events", server.URL, oid), nil)
req.AddCookie(cookie)
resp, err = client.Do(req)
require.NoError(t, err)
Expand All @@ -574,7 +574,7 @@ func TestOrganizationCRUD(t *testing.T) {
require.NoError(t, err)

// Delete org
req, _ = http.NewRequest("DELETE", fmt.Sprintf("%s/v1/organizations/%s", server.URL, oid), nil)
req, _ = http.NewRequest("DELETE", fmt.Sprintf("%s/api/v1/organizations/%s", server.URL, oid), nil)
req.AddCookie(cookie)
resp, err = client.Do(req)
require.NoError(t, err)
Expand Down Expand Up @@ -625,7 +625,7 @@ func TestAddUser(t *testing.T) {
cookie := &http.Cookie{Name: "capy_auth", Value: tokenString}

// Fetch user via API
req, _ := http.NewRequest("GET", fmt.Sprintf("%s/v1/users/%s", server.URL, createdUser.Uid.String()), nil)
req, _ := http.NewRequest("GET", fmt.Sprintf("%s/api/v1/users/%s", server.URL, createdUser.Uid.String()), nil)
req.AddCookie(cookie)
resp, err := client.Do(req)
require.NoError(t, err)
Expand Down Expand Up @@ -677,7 +677,7 @@ func TestAddDuplicateUser(t *testing.T) {
cookie := &http.Cookie{Name: "capy_auth", Value: tokenString}

// Fetch via API and verify UID matches
req, _ := http.NewRequest("GET", fmt.Sprintf("%s/v1/users/%s", server.URL, addedUser.Uid.String()), nil)
req, _ := http.NewRequest("GET", fmt.Sprintf("%s/api/v1/users/%s", server.URL, addedUser.Uid.String()), nil)
req.AddCookie(cookie)
resp, err := client.Do(req)
require.NoError(t, err)
Expand Down
Loading