@etus/bhono-app 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +0 -0
- package/package.json +7 -2
- package/templates/base/.claude/commands/check-skill-rules.md +112 -29
- package/templates/base/.claude/commands/linear/implement-issue.md +383 -55
- package/templates/base/.claude/commands/ship.md +77 -13
- package/templates/base/.claude/hooks/package-lock.json +0 -419
- package/templates/base/.claude/hooks/skill-activation-prompt.ts +185 -113
- package/templates/base/.claude/hooks/skill-tool-guard.sh +6 -0
- package/templates/base/.claude/hooks/skill-tool-guard.ts +198 -0
- package/templates/base/.claude/scripts/validate-skill-rules.sh +55 -32
- package/templates/base/.claude/settings.json +18 -11
- package/templates/base/.claude/skills/skill-rules.json +326 -173
- package/templates/base/.env.example +3 -0
- package/templates/base/CLAUDE.md +5 -5
- package/templates/base/README.md +40 -27
- package/templates/base/config/eslint.config.js +1 -0
- package/templates/base/config/wrangler.json +16 -17
- package/templates/base/docs/SETUP-GUIDE.md +566 -0
- package/templates/base/docs/app_spec.txt +13 -10
- package/templates/base/docs/architecture/README.md +162 -5
- package/templates/base/docs/architecture/api-catalog.md +575 -0
- package/templates/base/docs/architecture/c4-component.md +309 -0
- package/templates/base/docs/architecture/c4-container.md +183 -0
- package/templates/base/docs/architecture/c4-context.md +106 -0
- package/templates/base/docs/architecture/data-requirements.md +4 -3
- package/templates/base/docs/architecture/db-bootstrap.md +39 -0
- package/templates/base/docs/architecture/dependencies.md +327 -0
- package/templates/base/docs/architecture/drizzle-migration-plan.md +125 -0
- package/templates/base/docs/architecture/erd.md +1 -1
- package/templates/base/docs/architecture/sql-standards.md +100 -0
- package/templates/base/docs/architecture/tech-debt.md +184 -0
- package/templates/base/docs/testing.md +36 -29
- package/templates/base/package.json +26 -20
- package/templates/base/schema.sql +84 -0
- package/templates/base/scripts/capture-prod-session.ts +2 -2
- package/templates/base/scripts/init.sh +244 -59
- package/templates/base/scripts/sync-template.sh +104 -0
- package/templates/base/src/client/hooks/use-auth.ts +5 -0
- package/templates/base/src/client/routes/_authenticated/dashboard.tsx +1 -1
- package/templates/base/src/client/routes/index.tsx +1 -1
- package/templates/base/src/server/db/client.ts +3 -5
- package/templates/base/src/server/db/records.ts +81 -0
- package/templates/base/src/server/db/seed.ts +3 -2
- package/templates/base/src/server/db/sql.ts +116 -0
- package/templates/base/src/server/index.ts +17 -2
- package/templates/base/src/server/lib/audit.ts +74 -26
- package/templates/base/src/server/lib/audited-db.ts +219 -109
- package/templates/base/src/server/lib/transaction.ts +10 -16
- package/templates/base/src/server/middleware/account.ts +9 -16
- package/templates/base/src/server/middleware/auth.ts +102 -38
- package/templates/base/src/server/middleware/rate-limit.ts +8 -6
- package/templates/base/src/server/routes/accounts/handlers.ts +18 -6
- package/templates/base/src/server/routes/audits/handlers.ts +3 -1
- package/templates/base/src/server/routes/auth/handlers.ts +15 -10
- package/templates/base/src/server/routes/auth/test-login.ts +99 -45
- package/templates/base/src/server/routes/health/handlers.ts +4 -4
- package/templates/base/src/server/routes/index.ts +9 -0
- package/templates/base/src/server/routes/invitations/handlers.ts +9 -6
- package/templates/base/src/server/routes/openapi.ts +1 -1
- package/templates/base/src/server/routes/users/handlers.ts +21 -14
- package/templates/base/src/server/services/accounts.ts +242 -217
- package/templates/base/src/server/services/audits.ts +114 -61
- package/templates/base/src/server/services/auth.ts +310 -180
- package/templates/base/src/server/services/invitations.ts +282 -222
- package/templates/base/src/server/services/users.ts +383 -293
- package/templates/base/src/server/types/index.ts +1 -2
- package/templates/base/src/shared/types/api.ts +66 -198
- package/templates/base/tests/e2e/auth.setup.ts +1 -1
- package/templates/base/{src/server/__tests__/fixtures.ts → tests/fixtures/server.ts} +3 -3
- package/templates/base/{src/client/__tests__/setup-browser.ts → tests/helpers/client-setup-browser.ts} +2 -2
- package/templates/base/{src/client/__tests__/setup.ts → tests/helpers/client-setup.ts} +1 -1
- package/templates/base/{src/client/__tests__/test-utils.tsx → tests/helpers/client-test-utils.tsx} +2 -2
- package/templates/base/{src/server/__tests__/setup.ts → tests/helpers/server.ts} +9 -9
- package/templates/base/tests/integration/accounts/crud.test.ts +2 -11
- package/templates/base/tests/integration/audits/list.test.ts +2 -11
- package/templates/base/tests/integration/auth/auth-service.test.ts +1 -10
- package/templates/base/tests/integration/auth/invitation-token.test.ts +2 -11
- package/templates/base/tests/integration/auth/logout.test.ts +2 -11
- package/templates/base/tests/integration/auth/oauth.test.ts +23 -42
- package/templates/base/tests/integration/auth/refresh-token.test.ts +1 -9
- package/templates/base/tests/integration/auth/session-expiry.test.ts +1 -9
- package/templates/base/tests/integration/auth/session.test.ts +2 -11
- package/templates/base/tests/integration/auth/super-admin.test.ts +1 -9
- package/templates/base/tests/integration/authorization/analytics-role.test.ts +2 -11
- package/templates/base/tests/integration/authorization/billing-role.test.ts +2 -11
- package/templates/base/tests/integration/authorization/guards-roles.test.ts +1 -9
- package/templates/base/tests/integration/authorization/multi-tenancy.test.ts +2 -11
- package/templates/base/tests/integration/authorization/roles.test.ts +2 -11
- package/templates/base/tests/integration/config/production-behavior.test.ts +2 -11
- package/templates/base/tests/integration/health/health.test.ts +25 -44
- package/templates/base/tests/integration/invitations/crud.test.ts +2 -11
- package/templates/base/tests/integration/invitations/email.test.ts +1 -9
- package/templates/base/tests/integration/middleware/auth.test.ts +3 -12
- package/templates/base/tests/integration/middleware/request-logger.test.ts +1 -9
- package/templates/base/tests/integration/performance/response-times.test.ts +1 -9
- package/templates/base/tests/integration/security/cookie-security.test.ts +2 -11
- package/templates/base/tests/integration/security/csrf-protection.test.ts +2 -11
- package/templates/base/tests/integration/security/log-sanitization.test.ts +1 -9
- package/templates/base/tests/integration/security/rate-limiting.test.ts +1 -9
- package/templates/base/tests/integration/security/sql-injection.test.ts +7 -18
- package/templates/base/tests/integration/security/xss-prevention.test.ts +2 -11
- package/templates/base/tests/integration/setup.ts +13 -90
- package/templates/base/tests/integration/smoke.test.ts +3 -2
- package/templates/base/tests/integration/storage/upload.test.ts +2 -11
- package/templates/base/tests/integration/storage/validation.test.ts +2 -11
- package/templates/base/tests/integration/users/crud.test.ts +2 -11
- package/templates/base/tests/integration/users/list.test.ts +2 -11
- package/templates/base/tests/integration/vitest.config.ts +2 -9
- package/templates/base/{src/server/__tests__ → tests}/mocks/db.ts +1 -1
- package/templates/base/{src/server/__tests__ → tests}/mocks/index.ts +1 -1
- package/templates/base/{src/server/__tests__ → tests}/mocks/kv.ts +1 -1
- package/templates/base/{src/server/__tests__ → tests}/mocks/r2.ts +1 -1
- package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/sidebar.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/avatar.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client/components/ui}/button.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/card.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/dialog.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/input.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/loading-skeleton.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/skeleton.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/sonner.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/tabs.test.tsx +1 -1
- package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/account.test.tsx +1 -1
- package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/integrations.test.tsx +1 -1
- package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/settings.test.tsx +1 -1
- package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/team.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/authenticated-layout.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/dashboard.test.tsx +1 -1
- package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/invite.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/login.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/navigation.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/root-layout.test.tsx +1 -1
- package/templates/base/{src/server/auth/__tests__ → tests/unit/server/auth}/guards.test.ts +2 -2
- package/templates/base/{src → tests/unit}/server/auth/permissions.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/auth/roles.test.ts +1 -1
- package/templates/base/tests/unit/server/db/sql.test.ts +68 -0
- package/templates/base/{src → tests/unit}/server/env.test.ts +1 -1
- package/templates/base/tests/unit/server/lib/audited-db.test.ts +78 -0
- package/templates/base/{src → tests/unit}/server/lib/email.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/errors.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/oauth.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/pagination.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/password.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/providers.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/r2-storage.test.ts +2 -2
- package/templates/base/{src → tests/unit}/server/lib/session.test.ts +2 -2
- package/templates/base/{src → tests/unit}/server/lib/tokens.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/transaction.test.ts +5 -14
- package/templates/base/{src → tests/unit}/server/middleware/account.test.ts +16 -24
- package/templates/base/tests/unit/server/middleware/auth.test.ts +647 -0
- package/templates/base/{src → tests/unit}/server/middleware/cors.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/middleware/error-handler.test.ts +2 -2
- package/templates/base/{src → tests/unit}/server/middleware/rate-limit.test.ts +3 -2
- package/templates/base/{src → tests/unit}/server/middleware/request-context.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/middleware/request-logger.test.ts +1 -1
- package/templates/base/{src/server/__tests__/mocks/__tests__ → tests/unit/server/mocks}/db.test.ts +1 -1
- package/templates/base/{src/server/__tests__/mocks/__tests__ → tests/unit/server/mocks}/kv.test.ts +1 -1
- package/templates/base/{src/server/__tests__/mocks/__tests__ → tests/unit/server/mocks}/r2.test.ts +1 -1
- package/templates/base/{src/server/routes/accounts/__tests__ → tests/unit/server/routes/accounts}/handlers.test.ts +12 -12
- package/templates/base/{src/server/routes/audits/__tests__ → tests/unit/server/routes/audits}/handlers.test.ts +11 -11
- package/templates/base/{src/server/routes/auth/__tests__ → tests/unit/server/routes/auth}/handlers.test.ts +124 -13
- package/templates/base/{src/server/routes/health/__tests__ → tests/unit/server/routes/health}/handlers.test.ts +27 -23
- package/templates/base/{src/server/routes/invitations/__tests__ → tests/unit/server/routes/invitations}/handlers.test.ts +14 -17
- package/templates/base/{src/server/routes/storage/__tests__ → tests/unit/server/routes/storage}/handlers.test.ts +6 -6
- package/templates/base/{src/server/routes/users/__tests__ → tests/unit/server/routes/users}/handlers.test.ts +81 -17
- package/templates/base/tests/unit/server/services/accounts.test.ts +406 -0
- package/templates/base/tests/unit/server/services/audits.test.ts +360 -0
- package/templates/base/tests/unit/server/services/auth.test.ts +656 -0
- package/templates/base/tests/unit/server/services/invitations.test.ts +343 -0
- package/templates/base/tests/unit/server/services/users.test.ts +706 -0
- package/templates/base/{src/shared/schemas/__tests__ → tests/unit/shared}/schemas.test.ts +1 -1
- package/templates/base/tsconfig.json +2 -1
- package/templates/base/vite.config.ts +3 -1
- package/templates/base/vitest.config.browser.ts +3 -2
- package/templates/base/vitest.config.frontend.ts +3 -2
- package/templates/base/vitest.config.ts +7 -14
- package/templates/base/.claude/settings.local.json +0 -11
- package/templates/base/.github/workflows/test.yml +0 -127
- package/templates/base/auth-setup-error.png +0 -0
- package/templates/base/config/drizzle.config.ts +0 -10
- package/templates/base/pnpm-lock.yaml +0 -8175
- package/templates/base/src/server/db/schema/accounts.ts +0 -20
- package/templates/base/src/server/db/schema/audit-logs.ts +0 -26
- package/templates/base/src/server/db/schema/index.ts +0 -7
- package/templates/base/src/server/db/schema/invitations.ts +0 -30
- package/templates/base/src/server/db/schema/refresh-tokens.ts +0 -22
- package/templates/base/src/server/db/schema/user-accounts.ts +0 -25
- package/templates/base/src/server/db/schema/users.ts +0 -33
- package/templates/base/src/server/lib/audited-db.test.ts +0 -107
- package/templates/base/src/server/lib/schema-helpers.ts +0 -16
- package/templates/base/src/server/middleware/auth.test.ts +0 -345
- package/templates/base/src/server/services/__tests__/accounts.test.ts +0 -764
- package/templates/base/src/server/services/__tests__/audits.test.ts +0 -235
- package/templates/base/src/server/services/__tests__/auth.test.ts +0 -765
- package/templates/base/src/server/services/__tests__/invitations.test.ts +0 -704
- package/templates/base/src/server/services/__tests__/users.test.ts +0 -755
- package/templates/base/tests/e2e/_auth/.gitkeep +0 -0
- package/templates/base/tests/integration/lib/schema-helpers.test.ts +0 -129
- package/templates/base/tsconfig.tsbuildinfo +0 -1
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-can-be-collapsed-by-default-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-expands-when-collapsed-and-expand-button-is-clicked-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-handles-logout-button-click-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-handles-navigation-clicks-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-hides-navigation-labels-when-collapsed-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-highlights-active-route-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-renders-sidebar-navigation-items-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-shows-keyboard-shortcut-hint-when-expanded-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-shows-user-info-when-authenticated-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-shows-user-initials-in-avatar-fallback-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/error-boundary.test.tsx +0 -0
- /package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/error-fallback.test.tsx +0 -0
- /package/templates/base/{src/client/hooks/__tests__ → tests/unit/client/hooks}/use-auth.test.tsx +0 -0
- /package/templates/base/{src/client/hooks/__tests__ → tests/unit/client/hooks}/use-theme.test.tsx +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-dashboard-stats-cards-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-quick-action-cards-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-recent-activity-section-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-user-first-name-in-welcome-message-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-user-information-in-sidebar-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-render-dashboard-when-authenticated-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-show-navigation-sidebar-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-unauthenticated-should-redirect-to-login-when-not-authenticated-1.png +0 -0
- /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-display-accept-invitation-button-1.png +0 -0
- /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-display-decline-button-linking-to-homepage-1.png +0 -0
- /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-display-invitation-details--email--workspace--role--1.png +0 -0
- /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-have-a-logo-link-to-homepage-1.png +0 -0
- /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-render-invitation-page-with-inviter-name-and-workspace-1.png +0 -0
- /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-show-terms-of-service-and-privacy-policy-links-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-display-Google-OAuth-login-button-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-have-a-link-back-to-home-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-render-login-content-without-waiting-for-authentication-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-render-login-page-at--login-route-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-show-Terms-of-Service-and-Privacy-Policy-links-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-trigger-OAuth-flow-when-clicking-login-button-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-404-handling-should-display-404-text-on-not-found-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-404-handling-should-have-navigation-options-on-404-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-404-handling-should-render-404-page-for-unknown-routes-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-authenticated-navigation-should-navigate-from-dashboard-to-account-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-authenticated-navigation-should-navigate-from-dashboard-to-integrations-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-authenticated-navigation-should-navigate-from-dashboard-to-settings-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-authenticated-navigation-should-navigate-from-dashboard-to-team-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-home-page-navigation-should-display-navigation-links-on-home-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-home-page-navigation-should-have-correct-link-destinations-on-home-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-allow-access-to-home-page-without-authentication-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-allow-access-to-login-page-without-authentication-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-redirect-unauthenticated-users-from-dashboard-to-login-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-redirect-unauthenticated-users-from-settings-to-login-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-redirect-unauthenticated-users-from-team-to-login-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-render-Active-Sessions-section-with-session-cards-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-render-page-with-correct-title--Account--1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-show-API-Access-section-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-show-Connected-Accounts-section-with-Google-connected-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-show-Danger-Zone-section-with-delete-button-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-show-Security-section-with-Two-Factor-Authentication-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-API-documentation-section-should-display-API-documentation-link-section-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-Create-Webhook-Dialog-should-have-Add-Webhook-trigger-button-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-category-filters-should-display-all-category-buttons-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-display-all-integration-cards-with-names-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-display-category-badges-on-cards-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-show-Configure-button-for-connected-integrations-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-show-Connect-button-for-not-connected-integrations-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-show-Connected-badge-for-connected-integrations-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-Available-Integrations-section-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-Webhooks-section-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-connected-count-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-page-description-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-search-input-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-render-with-correct-title--Integrations--1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-search-functionality-should-filter-integrations-based-on-search-query-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-search-functionality-should-search-by-description-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-search-functionality-should-show-no-results-message-when-search-has-no-matches-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-display-existing-webhook-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-display-webhook-events-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-display-webhook-last-delivery-info-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-display-webhook-success-status-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-have-Add-Webhook-button-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Account-tab-should-display-connected-accounts-section-with-Google-provider-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Account-tab-should-display-sessions-and-danger-zone-sections-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Notifications-tab-should-display-all-notification-toggle-options-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Notifications-tab-should-display-email-notifications-section-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Notifications-tab-should-display-toggle-switches-for-notification-options-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Notifications-tab-should-have-toggles-checked-by-default-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display--Save-Changes--button-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-personal-information-form-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-profile-picture-section-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-user-email-in-disabled-email-input-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-user-initials-in-avatar-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-user-name-in-the-name-input-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-page-rendering-should-display-all-three-tabs-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-page-rendering-should-display-page-description-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-page-rendering-should-render-with-correct-title--Settings--1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-tab-navigation-should-show-Profile-tab-as-default-active-tab-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-tab-navigation-should-switch-to-Account-tab-when-clicked-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-tab-navigation-should-switch-to-Notifications-tab-when-clicked-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-display-Active-Members-section-with-member-count-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-display-Pending-Invitations-section-with-invitation-details-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-have-invite-member-button-that-can-be-clicked-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-render-page-with-correct-title-and-description-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-render-search-input-that-filters-team-members-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-show-current-user-with---you---indicator-and-role-badge-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/error-components.test.tsx +0 -0
|
@@ -28,19 +28,10 @@ import { errorHandler } from '../../../src/server/middleware/error-handler'
|
|
|
28
28
|
import { sessionMiddleware } from '../../../src/server/lib/session'
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
* Creates a database
|
|
32
|
-
* The better-sqlite3 drizzle doesn't have execute, but D1 does
|
|
31
|
+
* Creates a D1-compatible database instance for tests
|
|
33
32
|
*/
|
|
34
33
|
function createTestDb() {
|
|
35
|
-
|
|
36
|
-
return new Proxy(db, {
|
|
37
|
-
get(target, prop) {
|
|
38
|
-
if (prop === 'execute') {
|
|
39
|
-
return target.run.bind(target)
|
|
40
|
-
}
|
|
41
|
-
return (target as any)[prop]
|
|
42
|
-
},
|
|
43
|
-
})
|
|
34
|
+
return getDb()
|
|
44
35
|
}
|
|
45
36
|
|
|
46
37
|
describe('Invitations CRUD Integration', () => {
|
|
@@ -28,15 +28,7 @@ import { sendInvitationEmail } from '../../../src/server/lib/email'
|
|
|
28
28
|
// ============================================================================
|
|
29
29
|
|
|
30
30
|
function createTestDb() {
|
|
31
|
-
|
|
32
|
-
return new Proxy(db, {
|
|
33
|
-
get(target, prop) {
|
|
34
|
-
if (prop === 'execute') {
|
|
35
|
-
return target.run.bind(target)
|
|
36
|
-
}
|
|
37
|
-
return (target as any)[prop]
|
|
38
|
-
},
|
|
39
|
-
})
|
|
31
|
+
return getDb()
|
|
40
32
|
}
|
|
41
33
|
|
|
42
34
|
describe('Invitation Email Integration Tests', () => {
|
|
@@ -20,19 +20,10 @@ import {
|
|
|
20
20
|
} from '../setup'
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* Creates a database
|
|
24
|
-
* The better-sqlite3 drizzle doesn't have execute, but D1 does
|
|
23
|
+
* Creates a D1-compatible database instance for tests
|
|
25
24
|
*/
|
|
26
25
|
function createTestDb() {
|
|
27
|
-
|
|
28
|
-
return new Proxy(db, {
|
|
29
|
-
get(target, prop) {
|
|
30
|
-
if (prop === 'execute') {
|
|
31
|
-
return (target as any).run.bind(target)
|
|
32
|
-
}
|
|
33
|
-
return (target as any)[prop]
|
|
34
|
-
},
|
|
35
|
-
})
|
|
26
|
+
return getDb()
|
|
36
27
|
}
|
|
37
28
|
|
|
38
29
|
describe('Auth Middleware', () => {
|
|
@@ -57,7 +48,7 @@ describe('Auth Middleware', () => {
|
|
|
57
48
|
const env = getEnv()
|
|
58
49
|
;(c as any).env = env
|
|
59
50
|
|
|
60
|
-
// Set up the db for middleware
|
|
51
|
+
// Set up the db for middleware
|
|
61
52
|
const db = createTestDb()
|
|
62
53
|
c.set('db', db)
|
|
63
54
|
|
|
@@ -21,15 +21,7 @@ import { errorHandler } from '../../../src/server/middleware/error-handler'
|
|
|
21
21
|
* Creates a database wrapper
|
|
22
22
|
*/
|
|
23
23
|
function createTestDb() {
|
|
24
|
-
|
|
25
|
-
return new Proxy(db, {
|
|
26
|
-
get(target, prop) {
|
|
27
|
-
if (prop === 'execute') {
|
|
28
|
-
return target.run.bind(target)
|
|
29
|
-
}
|
|
30
|
-
return (target as any)[prop]
|
|
31
|
-
},
|
|
32
|
-
})
|
|
24
|
+
return getDb()
|
|
33
25
|
}
|
|
34
26
|
|
|
35
27
|
describe('Request Logger Middleware Integration', () => {
|
|
@@ -36,15 +36,7 @@ const THRESHOLDS = {
|
|
|
36
36
|
* Creates a database wrapper that adds the `execute` method
|
|
37
37
|
*/
|
|
38
38
|
function createTestDb() {
|
|
39
|
-
|
|
40
|
-
return new Proxy(db, {
|
|
41
|
-
get(target, prop) {
|
|
42
|
-
if (prop === 'execute') {
|
|
43
|
-
return target.run.bind(target)
|
|
44
|
-
}
|
|
45
|
-
return (target as unknown as Record<string | symbol, unknown>)[prop]
|
|
46
|
-
},
|
|
47
|
-
})
|
|
39
|
+
return getDb()
|
|
48
40
|
}
|
|
49
41
|
|
|
50
42
|
/**
|
|
@@ -27,19 +27,10 @@ import { errorHandler } from '../../../src/server/middleware/error-handler'
|
|
|
27
27
|
import { sessionMiddleware } from '../../../src/server/lib/session'
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* Creates a database
|
|
31
|
-
* The better-sqlite3 drizzle doesn't have execute, but D1 does
|
|
30
|
+
* Creates a D1-compatible database instance for tests
|
|
32
31
|
*/
|
|
33
32
|
function createTestDb() {
|
|
34
|
-
|
|
35
|
-
return new Proxy(db, {
|
|
36
|
-
get(target, prop) {
|
|
37
|
-
if (prop === 'execute') {
|
|
38
|
-
return target.run.bind(target)
|
|
39
|
-
}
|
|
40
|
-
return (target as any)[prop]
|
|
41
|
-
},
|
|
42
|
-
})
|
|
33
|
+
return getDb()
|
|
43
34
|
}
|
|
44
35
|
|
|
45
36
|
describe('Session Cookie Security', () => {
|
|
@@ -24,19 +24,10 @@ import { configurableCors } from '../../../src/server/middleware/cors'
|
|
|
24
24
|
import { sessionMiddleware } from '../../../src/server/lib/session'
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* Creates a database
|
|
28
|
-
* The better-sqlite3 drizzle doesn't have execute, but D1 does
|
|
27
|
+
* Creates a D1-compatible database instance for tests
|
|
29
28
|
*/
|
|
30
29
|
function createTestDb() {
|
|
31
|
-
|
|
32
|
-
return new Proxy(db, {
|
|
33
|
-
get(target, prop) {
|
|
34
|
-
if (prop === 'execute') {
|
|
35
|
-
return target.run.bind(target)
|
|
36
|
-
}
|
|
37
|
-
return (target as any)[prop]
|
|
38
|
-
},
|
|
39
|
-
})
|
|
30
|
+
return getDb()
|
|
40
31
|
}
|
|
41
32
|
|
|
42
33
|
describe('CSRF Protection', () => {
|
|
@@ -30,15 +30,7 @@ import { sessionMiddleware } from '../../../src/server/lib/session'
|
|
|
30
30
|
* Creates a database wrapper that adds the `execute` method
|
|
31
31
|
*/
|
|
32
32
|
function createTestDb() {
|
|
33
|
-
|
|
34
|
-
return new Proxy(db, {
|
|
35
|
-
get(target, prop) {
|
|
36
|
-
if (prop === 'execute') {
|
|
37
|
-
return target.run.bind(target)
|
|
38
|
-
}
|
|
39
|
-
return (target as any)[prop]
|
|
40
|
-
},
|
|
41
|
-
})
|
|
33
|
+
return getDb()
|
|
42
34
|
}
|
|
43
35
|
|
|
44
36
|
describe('Log Sanitization', () => {
|
|
@@ -31,15 +31,7 @@ import {
|
|
|
31
31
|
* Creates a database wrapper that adds the `execute` method
|
|
32
32
|
*/
|
|
33
33
|
function createTestDb() {
|
|
34
|
-
|
|
35
|
-
return new Proxy(db, {
|
|
36
|
-
get(target, prop) {
|
|
37
|
-
if (prop === 'execute') {
|
|
38
|
-
return target.run.bind(target)
|
|
39
|
-
}
|
|
40
|
-
return (target as any)[prop]
|
|
41
|
-
},
|
|
42
|
-
})
|
|
34
|
+
return getDb()
|
|
43
35
|
}
|
|
44
36
|
|
|
45
37
|
describe('Rate Limiting', () => {
|
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
* SQL Injection Prevention Integration Tests
|
|
3
3
|
*
|
|
4
4
|
* Tests that the API properly prevents SQL injection attacks through:
|
|
5
|
-
* - Parameterized queries via
|
|
5
|
+
* - Parameterized queries via SQL helpers
|
|
6
6
|
* - Zod UUID validation for ID parameters
|
|
7
7
|
* - Safe handling of user input in search queries
|
|
8
8
|
*
|
|
9
|
-
* Since the codebase uses
|
|
10
|
-
* these tests should PASS - demonstrating
|
|
11
|
-
* is
|
|
9
|
+
* Since the codebase uses parameterized SQL queries,
|
|
10
|
+
* these tests should PASS - demonstrating SQL injection protection
|
|
11
|
+
* is in place by default.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { describe, it, expect, beforeAll } from 'vitest'
|
|
15
15
|
import { Hono } from 'hono'
|
|
16
|
-
import { getEnv, getSqlite, type TestEnv } from '../setup'
|
|
16
|
+
import { getEnv, getDb, getSqlite, type TestEnv } from '../setup'
|
|
17
17
|
import { createTestScenario } from '../fixtures'
|
|
18
18
|
import type { HonoEnv } from '../../../src/server/types'
|
|
19
19
|
import { api } from '../../../src/server/routes'
|
|
@@ -21,21 +21,10 @@ import { errorHandler } from '../../../src/server/middleware/error-handler'
|
|
|
21
21
|
import { sessionMiddleware } from '../../../src/server/lib/session'
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
* Creates a database
|
|
25
|
-
* The better-sqlite3 drizzle doesn't have execute, but D1 does
|
|
24
|
+
* Creates a D1-compatible database instance for tests
|
|
26
25
|
*/
|
|
27
26
|
function createTestDb() {
|
|
28
|
-
|
|
29
|
-
const { drizzle } = require('drizzle-orm/better-sqlite3')
|
|
30
|
-
const db = drizzle(sqlite)
|
|
31
|
-
return new Proxy(db, {
|
|
32
|
-
get(target, prop) {
|
|
33
|
-
if (prop === 'execute') {
|
|
34
|
-
return target.run.bind(target)
|
|
35
|
-
}
|
|
36
|
-
return (target as any)[prop]
|
|
37
|
-
},
|
|
38
|
-
})
|
|
27
|
+
return getDb()
|
|
39
28
|
}
|
|
40
29
|
|
|
41
30
|
describe('SQL Injection Prevention', () => {
|
|
@@ -23,19 +23,10 @@ import { errorHandler } from '../../../src/server/middleware/error-handler'
|
|
|
23
23
|
import { sessionMiddleware } from '../../../src/server/lib/session'
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* Creates a database
|
|
27
|
-
* The better-sqlite3 drizzle doesn't have execute, but D1 does
|
|
26
|
+
* Creates a D1-compatible database instance for tests
|
|
28
27
|
*/
|
|
29
28
|
function createTestDb() {
|
|
30
|
-
|
|
31
|
-
return new Proxy(db, {
|
|
32
|
-
get(target, prop) {
|
|
33
|
-
if (prop === 'execute') {
|
|
34
|
-
return target.run.bind(target)
|
|
35
|
-
}
|
|
36
|
-
return (target as any)[prop]
|
|
37
|
-
},
|
|
38
|
-
})
|
|
29
|
+
return getDb()
|
|
39
30
|
}
|
|
40
31
|
|
|
41
32
|
describe('XSS Prevention', () => {
|
|
@@ -10,12 +10,13 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import Database from 'better-sqlite3'
|
|
13
|
-
import {
|
|
13
|
+
import { readFileSync } from 'node:fs'
|
|
14
|
+
import { dirname, resolve } from 'node:path'
|
|
15
|
+
import { fileURLToPath } from 'node:url'
|
|
14
16
|
import { vi, beforeAll, afterAll, beforeEach, afterEach } from 'vitest'
|
|
15
17
|
import { Hono } from 'hono'
|
|
16
18
|
import type { Env } from '../../src/server/env'
|
|
17
19
|
import type { SessionData, HonoEnv } from '../../src/server/types'
|
|
18
|
-
import * as schema from '../../src/server/db/schema'
|
|
19
20
|
|
|
20
21
|
// ============================================================================
|
|
21
22
|
// TYPES
|
|
@@ -50,90 +51,15 @@ export interface MockR2Store {
|
|
|
50
51
|
// DATABASE SCHEMA SQL
|
|
51
52
|
// ============================================================================
|
|
52
53
|
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
id TEXT PRIMARY KEY,
|
|
57
|
-
google_id TEXT NOT NULL UNIQUE,
|
|
58
|
-
email TEXT NOT NULL,
|
|
59
|
-
name TEXT NOT NULL,
|
|
60
|
-
avatar_url TEXT,
|
|
61
|
-
status TEXT DEFAULT 'active' NOT NULL CHECK (status IN ('active', 'inactive')),
|
|
62
|
-
provider_ids TEXT DEFAULT '[]',
|
|
63
|
-
is_super_admin INTEGER DEFAULT 0 NOT NULL,
|
|
64
|
-
created_at TEXT DEFAULT (datetime('now')) NOT NULL,
|
|
65
|
-
updated_at TEXT DEFAULT (datetime('now')) NOT NULL,
|
|
66
|
-
deleted_at TEXT,
|
|
67
|
-
created_by_id TEXT REFERENCES users(id),
|
|
68
|
-
updated_by_id TEXT REFERENCES users(id),
|
|
69
|
-
deleted_by_id TEXT REFERENCES users(id)
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
-- Accounts table
|
|
73
|
-
CREATE TABLE IF NOT EXISTS accounts (
|
|
74
|
-
id TEXT PRIMARY KEY,
|
|
75
|
-
name TEXT NOT NULL,
|
|
76
|
-
description TEXT,
|
|
77
|
-
domain TEXT UNIQUE,
|
|
78
|
-
created_at TEXT DEFAULT (datetime('now')) NOT NULL,
|
|
79
|
-
updated_at TEXT DEFAULT (datetime('now')) NOT NULL,
|
|
80
|
-
deleted_at TEXT
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
-- User-Accounts junction table
|
|
84
|
-
CREATE TABLE IF NOT EXISTS user_accounts (
|
|
85
|
-
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
86
|
-
account_id TEXT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
|
|
87
|
-
role TEXT NOT NULL CHECK (role IN ('ADMIN', 'MANAGER', 'EDITOR', 'AUTHOR', 'VIEWER', 'BILLING', 'ANALYTICS')),
|
|
88
|
-
PRIMARY KEY (user_id, account_id)
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
-- Refresh tokens table
|
|
92
|
-
CREATE TABLE IF NOT EXISTS refresh_tokens (
|
|
93
|
-
id TEXT PRIMARY KEY,
|
|
94
|
-
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
95
|
-
token_hash TEXT NOT NULL,
|
|
96
|
-
expires_at INTEGER NOT NULL,
|
|
97
|
-
created_at INTEGER DEFAULT (unixepoch()) NOT NULL,
|
|
98
|
-
revoked_at INTEGER
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
-- Invitations table
|
|
102
|
-
CREATE TABLE IF NOT EXISTS invitations (
|
|
103
|
-
id TEXT PRIMARY KEY,
|
|
104
|
-
account_id TEXT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
|
|
105
|
-
email TEXT NOT NULL,
|
|
106
|
-
role TEXT NOT NULL CHECK (role IN ('ADMIN', 'MANAGER', 'EDITOR', 'AUTHOR', 'VIEWER', 'BILLING', 'ANALYTICS')),
|
|
107
|
-
token TEXT NOT NULL UNIQUE,
|
|
108
|
-
invited_by_id TEXT NOT NULL REFERENCES users(id),
|
|
109
|
-
expires_at TEXT NOT NULL,
|
|
110
|
-
accepted_at TEXT,
|
|
111
|
-
created_at TEXT DEFAULT (datetime('now')) NOT NULL
|
|
112
|
-
);
|
|
113
|
-
CREATE UNIQUE INDEX IF NOT EXISTS account_email_idx ON invitations(account_id, email);
|
|
114
|
-
|
|
115
|
-
-- Audit logs table
|
|
116
|
-
CREATE TABLE IF NOT EXISTS audit_logs (
|
|
117
|
-
id TEXT PRIMARY KEY,
|
|
118
|
-
transaction_id TEXT NOT NULL,
|
|
119
|
-
account_id TEXT REFERENCES accounts(id),
|
|
120
|
-
user_id TEXT REFERENCES users(id),
|
|
121
|
-
entity TEXT NOT NULL,
|
|
122
|
-
entity_id TEXT NOT NULL,
|
|
123
|
-
action TEXT NOT NULL CHECK (action IN ('INSERT', 'UPDATE', 'DELETE', 'LOGIN', 'LOGOUT', 'SIGNUP', 'TOKEN_REFRESH', 'LOGIN_FAILED')),
|
|
124
|
-
changes TEXT,
|
|
125
|
-
ip_address TEXT,
|
|
126
|
-
user_agent TEXT,
|
|
127
|
-
timestamp TEXT DEFAULT (datetime('now')) NOT NULL
|
|
128
|
-
);
|
|
129
|
-
`
|
|
54
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
55
|
+
const __dirname = dirname(__filename)
|
|
56
|
+
const SCHEMA_SQL = readFileSync(resolve(__dirname, '..', '..', 'schema.sql'), 'utf8')
|
|
130
57
|
|
|
131
58
|
// ============================================================================
|
|
132
59
|
// GLOBAL STATE
|
|
133
60
|
// ============================================================================
|
|
134
61
|
|
|
135
62
|
let sqliteDb: Database.Database | null = null
|
|
136
|
-
let drizzleDb: ReturnType<typeof drizzle> | null = null
|
|
137
63
|
let mockKV: MockKVStore | null = null
|
|
138
64
|
let mockR2: MockR2Store | null = null
|
|
139
65
|
let testEnv: TestEnv | null = null
|
|
@@ -144,8 +70,8 @@ let testEnv: TestEnv | null = null
|
|
|
144
70
|
|
|
145
71
|
/**
|
|
146
72
|
* Creates a D1-compatible wrapper around better-sqlite3
|
|
147
|
-
* This allows
|
|
148
|
-
*
|
|
73
|
+
* This allows our test helpers to use a Cloudflare D1-like API
|
|
74
|
+
* against an in-memory SQLite database.
|
|
149
75
|
*/
|
|
150
76
|
function createD1CompatibleWrapper(db: Database.Database): D1Database {
|
|
151
77
|
return {
|
|
@@ -582,13 +508,14 @@ export function getEnv(): TestEnv {
|
|
|
582
508
|
}
|
|
583
509
|
|
|
584
510
|
/**
|
|
585
|
-
* Get the
|
|
511
|
+
* Get the D1-compatible database instance
|
|
586
512
|
*/
|
|
587
|
-
export function getDb():
|
|
588
|
-
|
|
513
|
+
export function getDb(): D1Database {
|
|
514
|
+
const env = getEnv()
|
|
515
|
+
if (!env.DB) {
|
|
589
516
|
throw new Error('Database not initialized. Make sure beforeAll has run.')
|
|
590
517
|
}
|
|
591
|
-
return
|
|
518
|
+
return env.DB
|
|
592
519
|
}
|
|
593
520
|
|
|
594
521
|
/**
|
|
@@ -764,9 +691,6 @@ beforeAll(() => {
|
|
|
764
691
|
// Create D1-compatible wrapper
|
|
765
692
|
const d1Wrapper = createD1CompatibleWrapper(sqliteDb)
|
|
766
693
|
|
|
767
|
-
// Create Drizzle instance
|
|
768
|
-
drizzleDb = drizzle(sqliteDb, { schema })
|
|
769
|
-
|
|
770
694
|
// Create mock stores
|
|
771
695
|
mockKV = createMockKV()
|
|
772
696
|
mockR2 = createMockR2()
|
|
@@ -797,7 +721,6 @@ afterAll(() => {
|
|
|
797
721
|
sqliteDb = null
|
|
798
722
|
}
|
|
799
723
|
|
|
800
|
-
drizzleDb = null
|
|
801
724
|
mockKV = null
|
|
802
725
|
mockR2 = null
|
|
803
726
|
testEnv = null
|
|
@@ -55,10 +55,11 @@ describe('Integration Test Infrastructure', () => {
|
|
|
55
55
|
})
|
|
56
56
|
})
|
|
57
57
|
|
|
58
|
-
describe('
|
|
59
|
-
it('should have initialized
|
|
58
|
+
describe('D1 Database', () => {
|
|
59
|
+
it('should have initialized D1 database instance', () => {
|
|
60
60
|
const db = getDb()
|
|
61
61
|
expect(db).toBeDefined()
|
|
62
|
+
expect(typeof db.prepare).toBe('function')
|
|
62
63
|
})
|
|
63
64
|
})
|
|
64
65
|
|
|
@@ -34,19 +34,10 @@ import { sessionMiddleware } from '../../../src/server/lib/session'
|
|
|
34
34
|
// ============================================================================
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
* Creates a database
|
|
38
|
-
* The better-sqlite3 drizzle doesn't have execute, but D1 does
|
|
37
|
+
* Creates a D1-compatible database instance for tests
|
|
39
38
|
*/
|
|
40
39
|
function createTestDb() {
|
|
41
|
-
|
|
42
|
-
return new Proxy(db, {
|
|
43
|
-
get(target, prop) {
|
|
44
|
-
if (prop === 'execute') {
|
|
45
|
-
return target.run.bind(target)
|
|
46
|
-
}
|
|
47
|
-
return (target as any)[prop]
|
|
48
|
-
},
|
|
49
|
-
})
|
|
40
|
+
return getDb()
|
|
50
41
|
}
|
|
51
42
|
|
|
52
43
|
/**
|
|
@@ -31,19 +31,10 @@ import { sessionMiddleware } from '../../../src/server/lib/session'
|
|
|
31
31
|
// ============================================================================
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
* Creates a database
|
|
35
|
-
* The better-sqlite3 drizzle doesn't have execute, but D1 does
|
|
34
|
+
* Creates a D1-compatible database instance for tests
|
|
36
35
|
*/
|
|
37
36
|
function createTestDb() {
|
|
38
|
-
|
|
39
|
-
return new Proxy(db, {
|
|
40
|
-
get(target, prop) {
|
|
41
|
-
if (prop === 'execute') {
|
|
42
|
-
return target.run.bind(target)
|
|
43
|
-
}
|
|
44
|
-
return (target as any)[prop]
|
|
45
|
-
},
|
|
46
|
-
})
|
|
37
|
+
return getDb()
|
|
47
38
|
}
|
|
48
39
|
|
|
49
40
|
/**
|
|
@@ -26,19 +26,10 @@ import { errorHandler } from '../../../src/server/middleware/error-handler'
|
|
|
26
26
|
import { sessionMiddleware } from '../../../src/server/lib/session'
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
* Creates a database
|
|
30
|
-
* The better-sqlite3 drizzle doesn't have execute, but D1 does
|
|
29
|
+
* Creates a D1-compatible database instance for tests
|
|
31
30
|
*/
|
|
32
31
|
function createTestDb() {
|
|
33
|
-
|
|
34
|
-
return new Proxy(db, {
|
|
35
|
-
get(target, prop) {
|
|
36
|
-
if (prop === 'execute') {
|
|
37
|
-
return target.run.bind(target)
|
|
38
|
-
}
|
|
39
|
-
return (target as any)[prop]
|
|
40
|
-
},
|
|
41
|
-
})
|
|
32
|
+
return getDb()
|
|
42
33
|
}
|
|
43
34
|
|
|
44
35
|
describe('Users CRUD Integration', () => {
|
|
@@ -29,19 +29,10 @@ import { errorHandler } from '../../../src/server/middleware/error-handler'
|
|
|
29
29
|
import { sessionMiddleware } from '../../../src/server/lib/session'
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* Creates a database
|
|
33
|
-
* The better-sqlite3 drizzle doesn't have execute, but D1 does
|
|
32
|
+
* Creates a D1-compatible database instance for tests
|
|
34
33
|
*/
|
|
35
34
|
function createTestDb() {
|
|
36
|
-
|
|
37
|
-
return new Proxy(db, {
|
|
38
|
-
get(target, prop) {
|
|
39
|
-
if (prop === 'execute') {
|
|
40
|
-
return target.run.bind(target)
|
|
41
|
-
}
|
|
42
|
-
return (target as any)[prop]
|
|
43
|
-
},
|
|
44
|
-
})
|
|
35
|
+
return getDb()
|
|
45
36
|
}
|
|
46
37
|
|
|
47
38
|
describe('Users List Integration', () => {
|
|
@@ -10,6 +10,7 @@ export default defineConfig({
|
|
|
10
10
|
alias: {
|
|
11
11
|
'@server': `${projectRoot}/src/server`,
|
|
12
12
|
'@shared': `${projectRoot}/src/shared`,
|
|
13
|
+
'@tests': `${projectRoot}/tests`,
|
|
13
14
|
},
|
|
14
15
|
},
|
|
15
16
|
test: {
|
|
@@ -49,17 +50,10 @@ export default defineConfig({
|
|
|
49
50
|
// Database setup (not business logic)
|
|
50
51
|
'src/server/db/client.ts',
|
|
51
52
|
'src/server/db/seed.ts',
|
|
53
|
+
'src/server/db/records.ts',
|
|
52
54
|
// Barrel exports (just re-exports, no logic)
|
|
53
|
-
'src/server/db/schema/index.ts',
|
|
54
55
|
'src/server/services/index.ts',
|
|
55
56
|
'src/server/middleware/index.ts',
|
|
56
|
-
// Drizzle schema definitions (table structures, not business logic)
|
|
57
|
-
'src/server/db/schema/users.ts',
|
|
58
|
-
'src/server/db/schema/accounts.ts',
|
|
59
|
-
'src/server/db/schema/user-accounts.ts',
|
|
60
|
-
'src/server/db/schema/audit-logs.ts',
|
|
61
|
-
'src/server/db/schema/refresh-tokens.ts',
|
|
62
|
-
'src/server/db/schema/invitations.ts',
|
|
63
57
|
// API documentation (not testable)
|
|
64
58
|
'src/server/routes/api.ts',
|
|
65
59
|
// Dev-only endpoint (tested via E2E)
|
|
@@ -67,7 +61,6 @@ export default defineConfig({
|
|
|
67
61
|
// Hard to test in isolation (require external services/mocking)
|
|
68
62
|
'src/server/lib/providers.ts',
|
|
69
63
|
'src/server/lib/transaction.ts',
|
|
70
|
-
'src/server/lib/schema-helpers.ts',
|
|
71
64
|
],
|
|
72
65
|
thresholds: {
|
|
73
66
|
statements: 90,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Mock } from "vitest";
|
|
2
2
|
import { describe, it, expect, vi, beforeEach } from "vitest"
|
|
3
|
-
import { render, screen } from "
|
|
3
|
+
import { render, screen } from "@tests/helpers/client-test-utils"
|
|
4
4
|
import userEvent from "@testing-library/user-event"
|
|
5
5
|
import { Sidebar } from "../sidebar"
|
|
6
6
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from "vitest"
|
|
2
|
-
import { render, screen, fireEvent, waitFor } from "
|
|
2
|
+
import { render, screen, fireEvent, waitFor } from "@tests/helpers/client-test-utils"
|
|
3
3
|
import { createRef } from "react"
|
|
4
4
|
import { Avatar, AvatarImage, AvatarFallback } from "../avatar"
|
|
5
5
|
|
package/templates/base/{src/client/__tests__ → tests/unit/client/components/ui}/button.test.tsx
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from "vitest"
|
|
2
|
-
import { render, screen } from "
|
|
2
|
+
import { render, screen } from "@tests/helpers/client-test-utils"
|
|
3
3
|
import userEvent from "@testing-library/user-event"
|
|
4
4
|
import { Button } from "@/components/ui/button"
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from "vitest"
|
|
2
|
-
import { render, screen, waitFor } from "
|
|
2
|
+
import { render, screen, waitFor } from "@tests/helpers/client-test-utils"
|
|
3
3
|
import userEvent from "@testing-library/user-event"
|
|
4
4
|
import {
|
|
5
5
|
Dialog,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from "vitest"
|
|
2
|
-
import { render, screen } from "
|
|
2
|
+
import { render, screen } from "@tests/helpers/client-test-utils"
|
|
3
3
|
import userEvent from "@testing-library/user-event"
|
|
4
4
|
import { createRef } from "react"
|
|
5
5
|
import { Input } from "../input"
|