@urateam/dashboard 0.1.2 → 0.1.5

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.
Files changed (115) hide show
  1. package/dist/__tests__/audit.test.d.ts +2 -0
  2. package/dist/__tests__/audit.test.d.ts.map +1 -0
  3. package/dist/__tests__/audit.test.js +219 -0
  4. package/dist/__tests__/audit.test.js.map +1 -0
  5. package/dist/__tests__/auth-routes.test.d.ts +2 -0
  6. package/dist/__tests__/auth-routes.test.d.ts.map +1 -0
  7. package/dist/__tests__/auth-routes.test.js +116 -0
  8. package/dist/__tests__/auth-routes.test.js.map +1 -0
  9. package/dist/__tests__/bootstrap-integration.test.d.ts +2 -0
  10. package/dist/__tests__/bootstrap-integration.test.d.ts.map +1 -0
  11. package/dist/__tests__/bootstrap-integration.test.js +75 -0
  12. package/dist/__tests__/bootstrap-integration.test.js.map +1 -0
  13. package/dist/__tests__/cost-chart.test.d.ts +2 -0
  14. package/dist/__tests__/cost-chart.test.d.ts.map +1 -0
  15. package/dist/__tests__/cost-chart.test.js +51 -0
  16. package/dist/__tests__/cost-chart.test.js.map +1 -0
  17. package/dist/__tests__/cost.test.d.ts +2 -0
  18. package/dist/__tests__/cost.test.d.ts.map +1 -0
  19. package/dist/__tests__/cost.test.js +197 -0
  20. package/dist/__tests__/cost.test.js.map +1 -0
  21. package/dist/__tests__/helpers/license.d.ts +8 -0
  22. package/dist/__tests__/helpers/license.d.ts.map +1 -0
  23. package/dist/__tests__/helpers/license.js +71 -0
  24. package/dist/__tests__/helpers/license.js.map +1 -0
  25. package/dist/__tests__/rbac-middleware.test.d.ts +2 -0
  26. package/dist/__tests__/rbac-middleware.test.d.ts.map +1 -0
  27. package/dist/__tests__/rbac-middleware.test.js +59 -0
  28. package/dist/__tests__/rbac-middleware.test.js.map +1 -0
  29. package/dist/__tests__/retry-route.test.d.ts +2 -0
  30. package/dist/__tests__/retry-route.test.d.ts.map +1 -0
  31. package/dist/__tests__/retry-route.test.js +115 -0
  32. package/dist/__tests__/retry-route.test.js.map +1 -0
  33. package/dist/__tests__/sso-integration.test.d.ts +2 -0
  34. package/dist/__tests__/sso-integration.test.d.ts.map +1 -0
  35. package/dist/__tests__/sso-integration.test.js +91 -0
  36. package/dist/__tests__/sso-integration.test.js.map +1 -0
  37. package/dist/__tests__/sso-middleware.test.d.ts +2 -0
  38. package/dist/__tests__/sso-middleware.test.d.ts.map +1 -0
  39. package/dist/__tests__/sso-middleware.test.js +66 -0
  40. package/dist/__tests__/sso-middleware.test.js.map +1 -0
  41. package/dist/__tests__/users-routes.test.d.ts +2 -0
  42. package/dist/__tests__/users-routes.test.d.ts.map +1 -0
  43. package/dist/__tests__/users-routes.test.js +127 -0
  44. package/dist/__tests__/users-routes.test.js.map +1 -0
  45. package/dist/middleware/rbac.d.ts +4 -0
  46. package/dist/middleware/rbac.d.ts.map +1 -0
  47. package/dist/middleware/rbac.js +21 -0
  48. package/dist/middleware/rbac.js.map +1 -0
  49. package/dist/middleware/sso.d.ts +9 -0
  50. package/dist/middleware/sso.d.ts.map +1 -0
  51. package/dist/middleware/sso.js +47 -0
  52. package/dist/middleware/sso.js.map +1 -0
  53. package/dist/routes/audit.d.ts +4 -0
  54. package/dist/routes/audit.d.ts.map +1 -0
  55. package/dist/routes/audit.js +123 -0
  56. package/dist/routes/audit.js.map +1 -0
  57. package/dist/routes/auth.d.ts +10 -0
  58. package/dist/routes/auth.d.ts.map +1 -0
  59. package/dist/routes/auth.js +105 -0
  60. package/dist/routes/auth.js.map +1 -0
  61. package/dist/routes/config.d.ts.map +1 -1
  62. package/dist/routes/config.js +4 -2
  63. package/dist/routes/config.js.map +1 -1
  64. package/dist/routes/coordination.d.ts.map +1 -1
  65. package/dist/routes/coordination.js +5 -3
  66. package/dist/routes/coordination.js.map +1 -1
  67. package/dist/routes/cost.d.ts +10 -0
  68. package/dist/routes/cost.d.ts.map +1 -0
  69. package/dist/routes/cost.js +104 -0
  70. package/dist/routes/cost.js.map +1 -0
  71. package/dist/routes/errors.d.ts.map +1 -1
  72. package/dist/routes/errors.js +4 -2
  73. package/dist/routes/errors.js.map +1 -1
  74. package/dist/routes/runs.d.ts +9 -1
  75. package/dist/routes/runs.d.ts.map +1 -1
  76. package/dist/routes/runs.js +82 -9
  77. package/dist/routes/runs.js.map +1 -1
  78. package/dist/routes/tokens.d.ts.map +1 -1
  79. package/dist/routes/tokens.js +4 -2
  80. package/dist/routes/tokens.js.map +1 -1
  81. package/dist/routes/users.d.ts +8 -0
  82. package/dist/routes/users.d.ts.map +1 -0
  83. package/dist/routes/users.js +64 -0
  84. package/dist/routes/users.js.map +1 -0
  85. package/dist/server.d.ts +13 -1
  86. package/dist/server.d.ts.map +1 -1
  87. package/dist/server.js +65 -7
  88. package/dist/server.js.map +1 -1
  89. package/dist/static/style.css +648 -0
  90. package/dist/views/audit.d.ts +29 -0
  91. package/dist/views/audit.d.ts.map +1 -0
  92. package/dist/views/audit.js +144 -0
  93. package/dist/views/audit.js.map +1 -0
  94. package/dist/views/cost.d.ts +28 -0
  95. package/dist/views/cost.d.ts.map +1 -0
  96. package/dist/views/cost.js +173 -0
  97. package/dist/views/cost.js.map +1 -0
  98. package/dist/views/forbidden.d.ts +7 -0
  99. package/dist/views/forbidden.d.ts.map +1 -0
  100. package/dist/views/forbidden.js +18 -0
  101. package/dist/views/forbidden.js.map +1 -0
  102. package/dist/views/layout.d.ts +11 -1
  103. package/dist/views/layout.d.ts.map +1 -1
  104. package/dist/views/layout.js +12 -18
  105. package/dist/views/layout.js.map +1 -1
  106. package/dist/views/run-detail.d.ts +1 -1
  107. package/dist/views/run-detail.d.ts.map +1 -1
  108. package/dist/views/run-detail.js +6 -1
  109. package/dist/views/run-detail.js.map +1 -1
  110. package/dist/views/users.d.ts +16 -0
  111. package/dist/views/users.d.ts.map +1 -0
  112. package/dist/views/users.js +44 -0
  113. package/dist/views/users.js.map +1 -0
  114. package/package.json +7 -5
  115. package/LICENSE +0 -27
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=audit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/audit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,219 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { generateKeyPairSync, sign } from "node:crypto";
3
+ import { Hono } from "hono";
4
+ import { createDashboard } from "../server.js";
5
+ import { createDb } from "@urateam/core";
6
+ import { auditEvents } from "@urateam/core/dist/db/schema.js";
7
+ import { _resetLicenseCache } from "@urateam/core/dist/license.js";
8
+ // ---------------- license test helper (inlined) ----------------
9
+ function b64url(buf) {
10
+ return buf
11
+ .toString("base64")
12
+ .replace(/\+/g, "-")
13
+ .replace(/\//g, "_")
14
+ .replace(/=+$/, "");
15
+ }
16
+ function makeJwt(privateKey, payload) {
17
+ const header = { alg: "EdDSA", typ: "JWT" };
18
+ const headerB64 = b64url(Buffer.from(JSON.stringify(header)));
19
+ const payloadB64 = b64url(Buffer.from(JSON.stringify(payload)));
20
+ const signingInput = `${headerB64}.${payloadB64}`;
21
+ const sig = sign(null, Buffer.from(signingInput), privateKey);
22
+ return `${signingInput}.${b64url(sig)}`;
23
+ }
24
+ let savedPublicKey;
25
+ let savedEnv;
26
+ async function installEnterpriseLicense() {
27
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519");
28
+ const publicKeyB64 = Buffer.from(publicKey.export({ format: "der", type: "spki" })).toString("base64");
29
+ const mod = await import("@urateam/core/dist/license-public-key.js");
30
+ if (savedPublicKey === undefined) {
31
+ savedPublicKey = mod
32
+ .LICENSE_PUBLIC_KEY_DER_B64;
33
+ }
34
+ Object.defineProperty(mod, "LICENSE_PUBLIC_KEY_DER_B64", {
35
+ value: publicKeyB64,
36
+ writable: true,
37
+ configurable: true,
38
+ });
39
+ const now = Math.floor(Date.now() / 1000);
40
+ const jwt = makeJwt(privateKey, {
41
+ iss: "urateams.com",
42
+ sub: "cust_test",
43
+ tier: "enterprise",
44
+ seats: 25,
45
+ iat: now,
46
+ exp: now + 86_400,
47
+ });
48
+ if (savedEnv === undefined)
49
+ savedEnv = process.env.URATEAM_LICENSE_KEY;
50
+ process.env.URATEAM_LICENSE_KEY = jwt;
51
+ _resetLicenseCache();
52
+ }
53
+ async function restoreLicense() {
54
+ if (savedPublicKey !== undefined) {
55
+ const mod = await import("@urateam/core/dist/license-public-key.js");
56
+ Object.defineProperty(mod, "LICENSE_PUBLIC_KEY_DER_B64", {
57
+ value: savedPublicKey,
58
+ writable: true,
59
+ configurable: true,
60
+ });
61
+ savedPublicKey = undefined;
62
+ }
63
+ if (savedEnv === undefined) {
64
+ delete process.env.URATEAM_LICENSE_KEY;
65
+ }
66
+ else {
67
+ process.env.URATEAM_LICENSE_KEY = savedEnv;
68
+ savedEnv = undefined;
69
+ }
70
+ _resetLicenseCache();
71
+ }
72
+ function basicAuthHeader(u, p) {
73
+ return "Basic " + Buffer.from(`${u}:${p}`).toString("base64");
74
+ }
75
+ const AUTH = { Authorization: basicAuthHeader("admin", "secret") };
76
+ async function seedAuditEvent(db) {
77
+ await db.insert(auditEvents).values({
78
+ id: "evt-test-1",
79
+ timestamp: new Date(),
80
+ eventType: "pm.issue_promoted",
81
+ actor: "pm-agent",
82
+ actorType: "pm-agent",
83
+ scope: "pm",
84
+ runId: null,
85
+ issueId: "ISS-1",
86
+ inputTokens: 0,
87
+ outputTokens: 0,
88
+ payload: JSON.stringify({ reason: "ready" }),
89
+ });
90
+ }
91
+ // ---------------- tests ----------------
92
+ describe("audit route — unlicensed", () => {
93
+ beforeEach(async () => {
94
+ await restoreLicense(); // ensure no license
95
+ });
96
+ it("returns 404 for GET /audit when feature is not licensed", async () => {
97
+ const db = await createDb({ connectionString: ":memory:" });
98
+ const app = createDashboard({
99
+ db,
100
+ pipelineConfigs: {},
101
+ repoConfigs: {},
102
+ auth: { username: "admin", password: "secret" },
103
+ });
104
+ const res = await app.request("/audit", { headers: AUTH });
105
+ expect(res.status).toBe(404);
106
+ });
107
+ it("returns 404 for GET /audit/export.csv when feature is not licensed", async () => {
108
+ const db = await createDb({ connectionString: ":memory:" });
109
+ const app = createDashboard({
110
+ db,
111
+ pipelineConfigs: {},
112
+ repoConfigs: {},
113
+ auth: { username: "admin", password: "secret" },
114
+ });
115
+ const res = await app.request("/audit/export.csv", { headers: AUTH });
116
+ expect(res.status).toBe(404);
117
+ });
118
+ });
119
+ describe("audit route — licensed (enterprise)", () => {
120
+ beforeEach(async () => {
121
+ await installEnterpriseLicense();
122
+ });
123
+ afterEach(async () => {
124
+ await restoreLicense();
125
+ });
126
+ // Enterprise tier enables `rbac`, so `requirePermission("audit.view")`
127
+ // requires a `user` in context. Wrap the dashboard in an outer Hono and
128
+ // inject a stub admin user so tests that run in enterprise mode pass.
129
+ function wrapWithAdminUser(inner) {
130
+ const outer = new Hono();
131
+ outer.use("*", async (c, next) => {
132
+ c.set("user", {
133
+ id: "u_admin",
134
+ email: "admin@b.com",
135
+ role: "admin",
136
+ });
137
+ await next();
138
+ });
139
+ outer.route("/", inner);
140
+ return outer;
141
+ }
142
+ it("GET /audit returns 200 and renders a seeded event", async () => {
143
+ const db = await createDb({ connectionString: ":memory:" });
144
+ await seedAuditEvent(db);
145
+ const app = wrapWithAdminUser(createDashboard({
146
+ db,
147
+ pipelineConfigs: {},
148
+ repoConfigs: {},
149
+ auth: { username: "admin", password: "secret" },
150
+ }));
151
+ const res = await app.request("/audit", { headers: AUTH });
152
+ expect(res.status).toBe(200);
153
+ const html = await res.text();
154
+ expect(html).toContain("pm.issue_promoted");
155
+ expect(html).toContain("Audit Log");
156
+ });
157
+ it("GET /audit/event/:id returns detail row with full payload", async () => {
158
+ const db = await createDb({ connectionString: ":memory:" });
159
+ await seedAuditEvent(db);
160
+ const app = wrapWithAdminUser(createDashboard({
161
+ db,
162
+ pipelineConfigs: {},
163
+ repoConfigs: {},
164
+ auth: { username: "admin", password: "secret" },
165
+ }));
166
+ const res = await app.request("/audit/event/evt-test-1", { headers: AUTH });
167
+ expect(res.status).toBe(200);
168
+ const html = await res.text();
169
+ expect(html).toContain("audit-detail-row");
170
+ expect(html).toContain("evt-test-1");
171
+ // Full payload JSON must be present (formatted with 2-space indent).
172
+ expect(html).toContain(""reason": "ready"");
173
+ });
174
+ it("GET /audit/event/:id returns 404 for missing event", async () => {
175
+ const db = await createDb({ connectionString: ":memory:" });
176
+ const app = wrapWithAdminUser(createDashboard({
177
+ db,
178
+ pipelineConfigs: {},
179
+ repoConfigs: {},
180
+ auth: { username: "admin", password: "secret" },
181
+ }));
182
+ // Return 200 (not 404) so HTMX renders the error fragment in the DOM.
183
+ const res = await app.request("/audit/event/does-not-exist", { headers: AUTH });
184
+ expect(res.status).toBe(200);
185
+ const html = await res.text();
186
+ expect(html).toContain("Event not found");
187
+ });
188
+ it("GET /audit includes expand button with hx-get pointing to /audit/event/:id", async () => {
189
+ const db = await createDb({ connectionString: ":memory:" });
190
+ await seedAuditEvent(db);
191
+ const app = wrapWithAdminUser(createDashboard({
192
+ db,
193
+ pipelineConfigs: {},
194
+ repoConfigs: {},
195
+ auth: { username: "admin", password: "secret" },
196
+ }));
197
+ const res = await app.request("/audit", { headers: AUTH });
198
+ const html = await res.text();
199
+ expect(html).toContain('hx-get="/audit/event/evt-test-1"');
200
+ expect(html).toContain('hx-swap="afterend"');
201
+ });
202
+ it("GET /audit/export.csv returns 200 text/csv with the header row", async () => {
203
+ const db = await createDb({ connectionString: ":memory:" });
204
+ await seedAuditEvent(db);
205
+ const app = wrapWithAdminUser(createDashboard({
206
+ db,
207
+ pipelineConfigs: {},
208
+ repoConfigs: {},
209
+ auth: { username: "admin", password: "secret" },
210
+ }));
211
+ const res = await app.request("/audit/export.csv", { headers: AUTH });
212
+ expect(res.status).toBe(200);
213
+ expect(res.headers.get("content-type") ?? "").toContain("text/csv");
214
+ const body = await res.text();
215
+ expect(body).toContain("timestamp_utc,event_type,actor,actor_type,scope,run_id,issue_id,input_tokens,output_tokens,payload_json");
216
+ expect(body).toContain("pm.issue_promoted");
217
+ });
218
+ });
219
+ //# sourceMappingURL=audit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.test.js","sourceRoot":"","sources":["../../src/__tests__/audit.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=auth-routes.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-routes.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/auth-routes.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,116 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { Hono } from "hono";
3
+ import { createDb, signState } from "@urateam/core";
4
+ import { auditEvents, dashboardSessions, } from "@urateam/core/dist/db/schema.js";
5
+ import { createAuthRouter } from "../routes/auth.js";
6
+ import { installTestProLicense, restoreLicense } from "./helpers/license.js";
7
+ let db;
8
+ const ssoConfig = {
9
+ enabled: true,
10
+ workosApiKey: "sk_test",
11
+ workosClientId: "client_test",
12
+ redirectUri: "https://x/auth/callback",
13
+ allowedDomain: undefined,
14
+ sessionDurationHours: 24,
15
+ cookieName: "urateam_session",
16
+ cookieSecure: false,
17
+ stateSigningSecret: "0123456789abcdef0123456789abcdef",
18
+ };
19
+ const stubWorkos = {
20
+ async getAuthorizationUrl(args) {
21
+ return `https://workos.example/auth?state=${args.state}&client=${args.clientId}`;
22
+ },
23
+ async authenticateWithCode(_args) {
24
+ return {
25
+ user: {
26
+ id: "wu_test",
27
+ email: "alice@acme.com",
28
+ firstName: "Alice",
29
+ lastName: "X",
30
+ },
31
+ };
32
+ },
33
+ };
34
+ beforeEach(async () => {
35
+ await installTestProLicense("enterprise");
36
+ db = await createDb({ connectionString: ":memory:" });
37
+ });
38
+ afterEach(async () => {
39
+ await restoreLicense();
40
+ });
41
+ function buildApp() {
42
+ const app = new Hono();
43
+ app.route("/", createAuthRouter({ db, sso: ssoConfig, workos: stubWorkos }));
44
+ return app;
45
+ }
46
+ // flush fire-and-forget audit writes
47
+ async function tick() {
48
+ await new Promise((r) => setImmediate(r));
49
+ await new Promise((r) => setImmediate(r));
50
+ }
51
+ describe("/auth/login", () => {
52
+ it("returns 302 to a workos url with a signed state", async () => {
53
+ const res = await buildApp().request("/auth/login?next=/runs");
54
+ expect(res.status).toBe(302);
55
+ const loc = res.headers.get("location");
56
+ expect(loc).toContain("workos.example");
57
+ expect(loc).toContain("state=");
58
+ });
59
+ });
60
+ describe("/auth/callback", () => {
61
+ it("creates a session, sets cookie, writes audit event, redirects to next", async () => {
62
+ const state = signState({ next: "/runs", nonce: "n" }, ssoConfig.stateSigningSecret);
63
+ const res = await buildApp().request(`/auth/callback?code=abc&state=${encodeURIComponent(state)}`);
64
+ expect(res.status).toBe(302);
65
+ expect(res.headers.get("location")).toBe("/runs");
66
+ const setCookie = res.headers.get("set-cookie");
67
+ expect(setCookie).toContain("urateam_session=");
68
+ expect(setCookie).toContain("HttpOnly");
69
+ const sessions = await db.select().from(dashboardSessions);
70
+ expect(sessions).toHaveLength(1);
71
+ await tick();
72
+ const events = await db.select().from(auditEvents);
73
+ expect(events.find((e) => e.eventType === "dashboard.login")).toBeDefined();
74
+ });
75
+ it("returns 400 on state mismatch", async () => {
76
+ const res = await buildApp().request(`/auth/callback?code=abc&state=garbage`);
77
+ expect(res.status).toBe(400);
78
+ });
79
+ it("returns 403 + audit event when allowedDomain rejects", async () => {
80
+ ssoConfig.allowedDomain = "evil.com";
81
+ const state = signState({ next: "/", nonce: "n" }, ssoConfig.stateSigningSecret);
82
+ const res = await buildApp().request(`/auth/callback?code=abc&state=${encodeURIComponent(state)}`);
83
+ expect(res.status).toBe(403);
84
+ await tick();
85
+ const events = await db.select().from(auditEvents);
86
+ expect(events.find((e) => e.eventType === "dashboard.login_denied")).toBeDefined();
87
+ ssoConfig.allowedDomain = undefined;
88
+ });
89
+ });
90
+ describe("/auth/logout", () => {
91
+ it("deletes session, clears cookie, writes logout event", async () => {
92
+ const state = signState({ next: "/", nonce: "n" }, ssoConfig.stateSigningSecret);
93
+ const cb = await buildApp().request(`/auth/callback?code=abc&state=${encodeURIComponent(state)}`);
94
+ const cookieHeader = cb.headers.get("set-cookie");
95
+ const sid = cookieHeader.match(/urateam_session=([^;]+)/)[1];
96
+ // Logout is now CSRF-protected at the server level: the dashboard
97
+ // middleware requires an HX-Request header on all state-changing
98
+ // routes. This test drives createAuthRouter directly (no middleware
99
+ // stack) but we send the header anyway to mirror the real client.
100
+ const res = await buildApp().request("/auth/logout", {
101
+ method: "POST",
102
+ headers: {
103
+ cookie: `urateam_session=${sid}`,
104
+ "HX-Request": "true",
105
+ },
106
+ });
107
+ expect(res.status).toBe(302);
108
+ expect(res.headers.get("location")).toBe("/auth/login");
109
+ const sessions = await db.select().from(dashboardSessions);
110
+ expect(sessions).toHaveLength(0);
111
+ await tick();
112
+ const events = await db.select().from(auditEvents);
113
+ expect(events.find((e) => e.eventType === "dashboard.logout")).toBeDefined();
114
+ });
115
+ });
116
+ //# sourceMappingURL=auth-routes.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-routes.test.js","sourceRoot":"","sources":["../../src/__tests__/auth-routes.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=bootstrap-integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap-integration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/bootstrap-integration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,75 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import { createDb, signState } from "@urateam/core";
3
+ import { dashboardUsers } from "@urateam/core/dist/db/schema.js";
4
+ import { Hono } from "hono";
5
+ import { createAuthRouter } from "../routes/auth.js";
6
+ import { installTestProLicense, restoreLicense } from "./helpers/license.js";
7
+ let db;
8
+ const ssoConfig = {
9
+ enabled: true,
10
+ workosApiKey: "sk_test",
11
+ workosClientId: "client_test",
12
+ redirectUri: "https://x/auth/callback",
13
+ allowedDomain: undefined,
14
+ sessionDurationHours: 24,
15
+ cookieName: "urateam_session",
16
+ cookieSecure: false,
17
+ stateSigningSecret: "0123456789abcdef0123456789abcdef",
18
+ };
19
+ const stubWorkos = {
20
+ async getAuthorizationUrl() {
21
+ return "https://workos.example/auth";
22
+ },
23
+ async authenticateWithCode() {
24
+ return {
25
+ user: {
26
+ id: "wu_1",
27
+ email: "alice@acme.com",
28
+ firstName: "Alice",
29
+ lastName: "X",
30
+ },
31
+ };
32
+ },
33
+ };
34
+ beforeEach(async () => {
35
+ await installTestProLicense("enterprise");
36
+ db = await createDb({ connectionString: ":memory:" });
37
+ });
38
+ afterEach(async () => {
39
+ await restoreLicense();
40
+ vi.unstubAllEnvs();
41
+ });
42
+ describe("SSO callback bootstrap admin integration", () => {
43
+ it("promotes alice@acme.com when URATEAM_ADMIN_EMAILS matches", async () => {
44
+ vi.stubEnv("URATEAM_ADMIN_EMAILS", "alice@acme.com");
45
+ const app = new Hono();
46
+ app.route("/", createAuthRouter({ db, sso: ssoConfig, workos: stubWorkos }));
47
+ const state = signState({ next: "/", nonce: "n" }, ssoConfig.stateSigningSecret);
48
+ const res = await app.request(`/auth/callback?code=abc&state=${encodeURIComponent(state)}`);
49
+ expect(res.status).toBe(302);
50
+ const users = await db.select().from(dashboardUsers);
51
+ expect(users).toHaveLength(1);
52
+ expect(users[0].role).toBe("admin");
53
+ });
54
+ it("leaves role as viewer when email does not match", async () => {
55
+ vi.stubEnv("URATEAM_ADMIN_EMAILS", "bob@acme.com");
56
+ const app = new Hono();
57
+ app.route("/", createAuthRouter({ db, sso: ssoConfig, workos: stubWorkos }));
58
+ const state = signState({ next: "/", nonce: "n" }, ssoConfig.stateSigningSecret);
59
+ const res = await app.request(`/auth/callback?code=abc&state=${encodeURIComponent(state)}`);
60
+ expect(res.status).toBe(302);
61
+ const users = await db.select().from(dashboardUsers);
62
+ expect(users[0].role).toBe("viewer");
63
+ });
64
+ it("does not promote when URATEAM_ADMIN_EMAILS is unset", async () => {
65
+ vi.stubEnv("URATEAM_ADMIN_EMAILS", "");
66
+ const app = new Hono();
67
+ app.route("/", createAuthRouter({ db, sso: ssoConfig, workos: stubWorkos }));
68
+ const state = signState({ next: "/", nonce: "n" }, ssoConfig.stateSigningSecret);
69
+ const res = await app.request(`/auth/callback?code=abc&state=${encodeURIComponent(state)}`);
70
+ expect(res.status).toBe(302);
71
+ const users = await db.select().from(dashboardUsers);
72
+ expect(users[0].role).toBe("viewer");
73
+ });
74
+ });
75
+ //# sourceMappingURL=bootstrap-integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap-integration.test.js","sourceRoot":"","sources":["../../src/__tests__/bootstrap-integration.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE7E,IAAI,EAAO,CAAC;AAEZ,MAAM,SAAS,GAAG;IAChB,OAAO,EAAE,IAAI;IACb,YAAY,EAAE,SAAS;IACvB,cAAc,EAAE,aAAa;IAC7B,WAAW,EAAE,yBAAyB;IACtC,aAAa,EAAE,SAA+B;IAC9C,oBAAoB,EAAE,EAAE;IACxB,UAAU,EAAE,iBAAiB;IAC7B,YAAY,EAAE,KAAK;IACnB,kBAAkB,EAAE,kCAAkC;CACvD,CAAC;AAEF,MAAM,UAAU,GAAG;IACjB,KAAK,CAAC,mBAAmB;QACvB,OAAO,6BAA6B,CAAC;IACvC,CAAC;IACD,KAAK,CAAC,oBAAoB;QACxB,OAAO;YACL,IAAI,EAAE;gBACJ,EAAE,EAAE,MAAM;gBACV,KAAK,EAAE,gBAAgB;gBACvB,SAAS,EAAE,OAAO;gBAClB,QAAQ,EAAE,GAAG;aACd;SACF,CAAC;IACJ,CAAC;CACK,CAAC;AAET,UAAU,CAAC,KAAK,IAAI,EAAE;IACpB,MAAM,qBAAqB,CAAC,YAAY,CAAC,CAAC;IAC1C,EAAE,GAAG,MAAM,QAAQ,CAAC,EAAE,gBAAgB,EAAE,UAAU,EAAE,CAAC,CAAC;AACxD,CAAC,CAAC,CAAC;AAEH,SAAS,CAAC,KAAK,IAAI,EAAE;IACnB,MAAM,cAAc,EAAE,CAAC;IACvB,EAAE,CAAC,aAAa,EAAE,CAAC;AACrB,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,0CAA0C,EAAE,GAAG,EAAE;IACxD,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QACzE,EAAE,CAAC,OAAO,CAAC,sBAAsB,EAAE,gBAAgB,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,GAAG,CAAC,KAAK,CACP,GAAG,EACH,gBAAgB,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,SAAgB,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CACpE,CAAC;QACF,MAAM,KAAK,GAAG,SAAS,CACrB,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,EACzB,SAAS,CAAC,kBAAkB,CAC7B,CAAC;QACF,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,OAAO,CAC3B,iCAAiC,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAC7D,CAAC;QACF,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACrD,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,EAAE,CAAC,OAAO,CAAC,sBAAsB,EAAE,cAAc,CAAC,CAAC;QACnD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,GAAG,CAAC,KAAK,CACP,GAAG,EACH,gBAAgB,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,SAAgB,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CACpE,CAAC;QACF,MAAM,KAAK,GAAG,SAAS,CACrB,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,EACzB,SAAS,CAAC,kBAAkB,CAC7B,CAAC;QACF,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,OAAO,CAC3B,iCAAiC,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAC7D,CAAC;QACF,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACrD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,EAAE,CAAC,OAAO,CAAC,sBAAsB,EAAE,EAAE,CAAC,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,GAAG,CAAC,KAAK,CACP,GAAG,EACH,gBAAgB,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,SAAgB,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CACpE,CAAC;QACF,MAAM,KAAK,GAAG,SAAS,CACrB,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,EACzB,SAAS,CAAC,kBAAkB,CAC7B,CAAC;QACF,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,OAAO,CAC3B,iCAAiC,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAC7D,CAAC;QACF,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACrD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cost-chart.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost-chart.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cost-chart.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,51 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { renderCostChart } from "../views/cost.js";
3
+ describe("renderCostChart", () => {
4
+ it("returns empty string for fewer than 2 data points", () => {
5
+ expect(renderCostChart([])).toBe("");
6
+ expect(renderCostChart([{ date: "2026-04-01", runs: 1, prsMerged: 1, dollars: 1, timeSavedHours: 1 }])).toBe("");
7
+ });
8
+ it("emits an SVG polyline with one point per day", () => {
9
+ const byDay = [
10
+ { date: "2026-04-01", runs: 1, prsMerged: 1, dollars: 10, timeSavedHours: 4 },
11
+ { date: "2026-04-02", runs: 2, prsMerged: 2, dollars: 20, timeSavedHours: 8 },
12
+ { date: "2026-04-03", runs: 1, prsMerged: 1, dollars: 5, timeSavedHours: 4 },
13
+ ];
14
+ const html = renderCostChart(byDay);
15
+ expect(html).toContain("<svg");
16
+ expect(html).toContain("<polyline");
17
+ // 3 points → 3 "x,y" pairs in the points attribute
18
+ const pointsMatch = html.match(/points="([^"]+)"/);
19
+ expect(pointsMatch).not.toBeNull();
20
+ const points = pointsMatch[1].trim().split(" ");
21
+ expect(points).toHaveLength(3);
22
+ // first and last dates rendered as header labels
23
+ expect(html).toContain("2026-04-01");
24
+ expect(html).toContain("2026-04-03");
25
+ // total + peak labels
26
+ expect(html).toContain("Total: $35.00");
27
+ expect(html).toContain("peak: $20.00");
28
+ });
29
+ it("handles a flat-zero series without divide-by-zero", () => {
30
+ const byDay = [
31
+ { date: "2026-04-01", runs: 0, prsMerged: 0, dollars: 0, timeSavedHours: 0 },
32
+ { date: "2026-04-02", runs: 0, prsMerged: 0, dollars: 0, timeSavedHours: 0 },
33
+ ];
34
+ const html = renderCostChart(byDay);
35
+ expect(html).toContain("<polyline");
36
+ // No NaN anywhere
37
+ expect(html).not.toMatch(/NaN/);
38
+ });
39
+ it("escapes HTML-sensitive date labels", () => {
40
+ // Dates are YYYY-MM-DD from the server so no real injection risk, but the
41
+ // renderer should still pass dates through escapeHtml to stay defensive.
42
+ const byDay = [
43
+ { date: "<script>", runs: 1, prsMerged: 1, dollars: 1, timeSavedHours: 1 },
44
+ { date: "2026-04-02", runs: 1, prsMerged: 1, dollars: 1, timeSavedHours: 1 },
45
+ ];
46
+ const html = renderCostChart(byDay);
47
+ expect(html).not.toContain("<script>");
48
+ expect(html).toContain("&lt;script&gt;");
49
+ });
50
+ });
51
+ //# sourceMappingURL=cost-chart.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost-chart.test.js","sourceRoot":"","sources":["../../src/__tests__/cost-chart.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cost.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cost.test.ts"],"names":[],"mappings":""}