package main import ( "bytes" "database/sql" "encoding/json" "io" "net/http" "net/http/httptest" "testing" "atlas9.dev/c/core" "atlas9.dev/c/core/dbi" "atlas9.dev/c/core/iam" "atlas9.dev/c/core/routes" ) type testServer struct { URL string DB *sql.DB } func setupTestServer(t *testing.T) *testServer { t.Helper() db := setupTestDB(t) groups := func(tx dbi.DBI) iam.GroupStore { return NewSqliteGroupStore(tx) } members := func(tx dbi.DBI) iam.MemberStore { return NewSqliteMemberStore(tx) } roles := func(tx dbi.DBI) iam.RoleStore { return NewSqliteRoleStore(tx) } grants := func(tx dbi.DBI) iam.GrantStore { return NewSqliteGrantStore(tx) } tenants := func(tx dbi.DBI) iam.TenantStore { return NewSqliteTenantStore(tx) } tenantMembers := func(tx dbi.DBI) iam.TenantMemberStore { return NewSqliteTenantMemberStore(tx) } groupSvc := &GroupApi{DB: db, Groups: groups} memberSvc := &MemberApi{DB: db, Members: members} roleSvc := &RoleApi{DB: db, Roles: roles} grantSvc := &GrantApi{DB: db, Grants: grants} tenantSvc := &TenantApi{DB: db, Tenants: tenants} tenantMemberSvc := &TenantMemberApi{DB: db, Members: tenantMembers} mux := http.NewServeMux() routes.Register(mux, GroupRoutes(groupSvc)) routes.Register(mux, MemberRoutes(memberSvc)) routes.Register(mux, RoleRoutes(roleSvc)) routes.Register(mux, GrantRoutes(grantSvc)) routes.Register(mux, TenantRoutes(tenantSvc)) routes.Register(mux, TenantMemberRoutes(tenantMemberSvc)) server := httptest.NewServer(mux) t.Cleanup(server.Close) return &testServer{URL: server.URL, DB: db} } type rpcResponse[T any] struct { Status int Body T Error string } func rpcCall[Req, Res any](t *testing.T, baseURL, path string, req Req) rpcResponse[Res] { t.Helper() body, err := json.Marshal(req) if err != nil { t.Fatalf("marshal request: %v", err) } resp, err := http.Post(baseURL+path, "application/json", bytes.NewReader(body)) if err != nil { t.Fatalf("POST %s: %v", path, err) } defer resp.Body.Close() respBody, err := io.ReadAll(resp.Body) if err != nil { t.Fatalf("read response: %v", err) } if resp.StatusCode != 200 { return rpcResponse[Res]{Status: resp.StatusCode, Error: string(respBody)} } // Check for error response (handler returned an error). var errResp routes.ErrorResponse json.Unmarshal(respBody, &errResp) if errResp.Message != "" { return rpcResponse[Res]{Status: resp.StatusCode, Error: errResp.Message} } var res Res if err := json.Unmarshal(respBody, &res); err != nil { t.Fatalf("unmarshal response: %v", err) } return rpcResponse[Res]{Status: resp.StatusCode, Body: res} } func mustRPC[Req, Res any](t *testing.T, baseURL, path string, req Req) Res { t.Helper() r := rpcCall[Req, Res](t, baseURL, path, req) if r.Error != "" { t.Fatalf("rpc %s: %s", path, r.Error) } return r.Body } func createTestUser(t *testing.T, db *sql.DB, id core.ID) { t.Helper() _, err := db.Exec( "INSERT INTO users (id, email, verified) VALUES ($1, $2, true)", id, id.String()+"@test.com", ) if err != nil { t.Fatal(err) } }