package tenant_test import ( "encoding/json" "net/http/httptest" "testing" "github.com/gofiber/fiber/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/techxcar/backend/internal/auth" "github.com/techxcar/backend/internal/config" "github.com/techxcar/backend/internal/tenant" ) const handlerTestSecret = "test-secret-32-chars-minimum-ok!" func buildAdminApp(repo *tenant.Repository, cfg *config.Config) *fiber.App { app := fiber.New(fiber.Config{ErrorHandler: func(c *fiber.Ctx, err error) error { code := fiber.StatusInternalServerError if e, ok := err.(*fiber.Error); ok { code = e.Code } return c.Status(code).JSON(fiber.Map{"data": nil, "error": err.Error()}) }}) tenant.RegisterRoutes(app, repo, nil, cfg) return app } func adminToken(t *testing.T, secret string) string { t.Helper() tok, err := auth.GenerateAccessToken("sa-1", "", "super_admin", secret) require.NoError(t, err) return tok } func TestListTenants_requiresAuth(t *testing.T) { db := setupDB(t) repo := tenant.NewRepository(db) cfg := &config.Config{JWTSecret: handlerTestSecret} app := buildAdminApp(repo, cfg) req := httptest.NewRequest("GET", "/api/v1/admin/tenants", nil) resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, 401, resp.StatusCode) } func TestListTenants_success(t *testing.T) { db := setupDB(t) repo := tenant.NewRepository(db) cfg := &config.Config{JWTSecret: handlerTestSecret} app := buildAdminApp(repo, cfg) req := httptest.NewRequest("GET", "/api/v1/admin/tenants", nil) req.Header.Set("Authorization", "Bearer "+adminToken(t, handlerTestSecret)) resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, 200, resp.StatusCode) var result map[string]any require.NoError(t, json.NewDecoder(resp.Body).Decode(&result)) assert.Nil(t, result["error"]) } func TestTenantAccessHandler(t *testing.T) { db := setupDB(t) repo := tenant.NewRepository(db) cfg := &config.Config{JWTSecret: handlerTestSecret} app := buildAdminApp(repo, cfg) ctx := t.Context() slug := "access-test-" + t.Name() ten, err := repo.CreateTenant(ctx, slug, "Tenant Access Test") require.NoError(t, err) t.Cleanup(func() { db.Pool.Exec(ctx, "DELETE FROM tenants WHERE id = $1", ten.ID) }) req := httptest.NewRequest("POST", "/api/v1/admin/tenants/"+ten.ID+"/access", nil) req.Header.Set("Authorization", "Bearer "+adminToken(t, handlerTestSecret)) resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, 200, resp.StatusCode) var body struct { Data struct { AccessToken string `json:"access_token"` Tenant struct { ID string `json:"id"` Name string `json:"name"` Slug string `json:"slug"` } `json:"tenant"` } `json:"data"` Error *string `json:"error"` } require.NoError(t, json.NewDecoder(resp.Body).Decode(&body)) assert.Nil(t, body.Error) assert.NotEmpty(t, body.Data.AccessToken) assert.Equal(t, ten.ID, body.Data.Tenant.ID) assert.Equal(t, "Tenant Access Test", body.Data.Tenant.Name) assert.Equal(t, slug, body.Data.Tenant.Slug) claims, err := auth.ValidateToken(body.Data.AccessToken, handlerTestSecret) require.NoError(t, err) assert.Equal(t, "sa-1", claims.UserID) assert.Equal(t, ten.ID, claims.TenantID) assert.Equal(t, "tenant_admin", claims.Role) } func TestTenantAccessHandler_notFound(t *testing.T) { db := setupDB(t) repo := tenant.NewRepository(db) cfg := &config.Config{JWTSecret: handlerTestSecret} app := buildAdminApp(repo, cfg) req := httptest.NewRequest("POST", "/api/v1/admin/tenants/nonexistent-id/access", nil) req.Header.Set("Authorization", "Bearer "+adminToken(t, handlerTestSecret)) resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, 404, resp.StatusCode) } func TestTenantAccessHandler_inactive(t *testing.T) { db := setupDB(t) repo := tenant.NewRepository(db) cfg := &config.Config{JWTSecret: handlerTestSecret} app := buildAdminApp(repo, cfg) ctx := t.Context() slug := "inactive-test-" + t.Name() ten, err := repo.CreateTenant(ctx, slug, "Tenant Inactive Test") require.NoError(t, err) t.Cleanup(func() { db.Pool.Exec(ctx, "DELETE FROM tenants WHERE id = $1", ten.ID) }) // Update tenant status to something other than "active" _, err = db.Pool.Exec(ctx, "UPDATE public.tenants SET status = 'suspended' WHERE id = $1", ten.ID) require.NoError(t, err) req := httptest.NewRequest("POST", "/api/v1/admin/tenants/"+ten.ID+"/access", nil) req.Header.Set("Authorization", "Bearer "+adminToken(t, handlerTestSecret)) resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, 404, resp.StatusCode) } func TestGetInvite_notFound(t *testing.T) { db := setupDB(t) repo := tenant.NewRepository(db) cfg := &config.Config{JWTSecret: handlerTestSecret} app := buildAdminApp(repo, cfg) req := httptest.NewRequest("GET", "/api/v1/invites/nonexistent-token", nil) resp, err := app.Test(req) require.NoError(t, err) assert.Equal(t, 404, resp.StatusCode) }