package server_test import ( "context" "net/http" "net/http/httptest" "os" "testing" "time" "github.com/jackc/pgx/v5/pgxpool" "chat-agent-system/internal/authz" "chat-agent-system/internal/config" "chat-agent-system/internal/server" ) func openPGPool(t *testing.T) *pgxpool.Pool { t.Helper() dsn := os.Getenv("TEST_DATABASE_URL") if dsn == "" { dsn = "postgres://app:dev_secret_db_pass@127.0.0.1:5433/agent_system_dev?sslmode=disable" } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() pool, err := pgxpool.New(ctx, dsn) if err != nil { t.Skipf("postgres unavailable: %v", err) return nil } if err := pool.Ping(ctx); err != nil { pool.Close() t.Skipf("postgres ping failed: %v", err) } return pool } func TestProjectMembers_CrossProjectForbidden(t *testing.T) { pool := openPGPool(t) if pool == nil { return } defer pool.Close() ctx := context.Background() var projectA, projectB int64 err := pool.QueryRow(ctx, `SELECT id FROM projects ORDER BY id ASC LIMIT 1`).Scan(&projectA) if err != nil || projectA <= 0 { t.Skip("no projects in database for cross-project test") } err = pool.QueryRow(ctx, `SELECT id FROM projects WHERE id <> $1 ORDER BY id ASC LIMIT 1`, projectA).Scan(&projectB) if err != nil || projectB <= 0 { t.Skip("need at least two projects for cross-project test") } var outsiderID int64 err = pool.QueryRow(ctx, ` SELECT u.id FROM users u WHERE NOT EXISTS (SELECT 1 FROM project_members pm WHERE pm.project_id = $1 AND pm.user_id = u.id AND pm.status = 'active') AND NOT EXISTS (SELECT 1 FROM projects p WHERE p.id = $1 AND p.creator_user_id = u.id) ORDER BY u.id ASC LIMIT 1 `, projectA).Scan(&outsiderID) if err != nil || outsiderID <= 0 { t.Skip("no outsider user available for cross-project test") } authzSvc := authz.NewAuthorizer(pool) if err := authzSvc.RequireProjectAccess(ctx, projectA, outsiderID); err != authz.ErrForbidden { t.Fatalf("expected outsider forbidden on project A, got %v", err) } } func TestProjectMembers_AnonymousReturns401(t *testing.T) { cfg := &config.Config{Env: "dev", AdminUsername: "admin", AdminPassword: "admin123"} srv := server.NewServer(cfg, nil) ts := httptest.NewServer(srv.Routes()) defer ts.Close() resp, err := http.Get(ts.URL + "/api/v1/projects/1/members") if err != nil { t.Fatalf("request: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusUnauthorized { t.Fatalf("expected 401, got %d", resp.StatusCode) } }