Skip to content

Commit 6594015

Browse files
hotlongCopilot
andcommitted
test: align tests with member-seeding and visibility merge
Two test-only fixes to match new behavior that already shipped: 1. service-tenant/project-provisioning.test.ts ProjectProvisioningService.provisionProject now also seeds a row in sys_project_member (creator → owner) so the dispatcher's per-user membership lookup returns the role immediately after creation. Test was still asserting only ['sys_project', 'sys_project_credential']; expand to ['sys_project', 'sys_project_credential', 'sys_project_member'] and assert the seeded owner row. 2. service-cloud/public-artifact-routes.test.ts Visibility merge collapsed 'unlisted' into 'private' with share-by-link semantics. Rewrite the visibility-gating tests: - 'private project: all public routes return 404' → split into enumeration-404 and ?commit=<id>-200 (share-by-link works anonymously). - 'private project: defaults when visibility is undefined' → asserts both branches (no-commit 404, with-commit 200). - Old 'unlisted' tests are kept as 'legacy unlisted rows behave like private' to exercise the coercion path on old DB rows. Full suite: pnpm test → 100/100 turbo tasks successful. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent f7038cc commit 6594015

2 files changed

Lines changed: 34 additions & 17 deletions

File tree

packages/services/service-cloud/test/public-artifact-routes.test.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
* Tests for the public, unauthenticated artifact API
55
* (`/pub/v1/projects/:id/*`) and its visibility gating.
66
*
7-
* - sys_project.visibility = 'private' → 404 on every public route
8-
* - sys_project.visibility = 'unlisted' → 404 on /artifact without ?commit=,
9-
* 200 on /artifact?commit=<id>,
10-
* 404 on /revisions and /manifest.json
7+
* - sys_project.visibility = 'private' → 404 on /artifact without ?commit=
8+
* (and on /revisions + /manifest.json),
9+
* 200 on /artifact?commit=<id> (share-by-link)
1110
* - sys_project.visibility = 'public' → 200 on all three routes
11+
* - sys_project.visibility = 'unlisted' (legacy) is coerced to 'private'
1212
*/
1313

1414
import { describe, it, expect, beforeEach } from 'vitest';
@@ -121,25 +121,29 @@ describe('public artifact API (/pub/v1/projects/:id/*)', () => {
121121
await plugin.init(ctx); await plugin.start(ctx);
122122
}
123123

124-
// --- private --------------------------------------------------------------
125-
it('private project: all public routes return 404 (no leak)', async () => {
124+
// --- private (share-by-link) ---------------------------------------------
125+
it('private project: enumeration routes return 404, but ?commit= returns 200', async () => {
126126
await boot([{ id: 'proj_a', organization_id: 'org_1', visibility: 'private' }]);
127127

128-
const a = await server.invoke('GET', '/api/v1/pub/v1/projects/proj_a/artifact');
129-
const b = await server.invoke('GET', '/api/v1/pub/v1/projects/proj_a/artifact', { query: { commit: 'cafebabe' } });
130-
const c = await server.invoke('GET', '/api/v1/pub/v1/projects/proj_a/revisions');
131-
const d = await server.invoke('GET', '/api/v1/pub/v1/projects/proj_a/manifest.json');
132-
expect([a.status, b.status, c.status, d.status]).toEqual([404, 404, 404, 404]);
128+
const enumArtifact = await server.invoke('GET', '/api/v1/pub/v1/projects/proj_a/artifact');
129+
const shareByLink = await server.invoke('GET', '/api/v1/pub/v1/projects/proj_a/artifact', { query: { commit: 'cafebabe' } });
130+
const revs = await server.invoke('GET', '/api/v1/pub/v1/projects/proj_a/revisions');
131+
const mani = await server.invoke('GET', '/api/v1/pub/v1/projects/proj_a/manifest.json');
132+
expect([enumArtifact.status, shareByLink.status, revs.status, mani.status]).toEqual([404, 200, 404, 404]);
133+
expect(shareByLink.body.data.commitId).toBe('cafebabe');
133134
});
134135

135136
it('private project: defaults when visibility is undefined', async () => {
136137
await boot([{ id: 'proj_a', organization_id: 'org_1' /* no visibility */ }]);
137-
const r = await server.invoke('GET', '/api/v1/pub/v1/projects/proj_a/artifact', { query: { commit: 'cafebabe' } });
138-
expect(r.status).toBe(404);
138+
// Defaulted-to-private: enumeration is hidden but share-by-link works.
139+
const noCommit = await server.invoke('GET', '/api/v1/pub/v1/projects/proj_a/artifact');
140+
expect(noCommit.status).toBe(404);
141+
const withCommit = await server.invoke('GET', '/api/v1/pub/v1/projects/proj_a/artifact', { query: { commit: 'cafebabe' } });
142+
expect(withCommit.status).toBe(200);
139143
});
140144

141-
// --- unlisted -------------------------------------------------------------
142-
it('unlisted project: 404 without ?commit=, 200 with ?commit=', async () => {
145+
// --- unlisted (legacy → coerced to private) ------------------------------
146+
it('legacy `unlisted` rows behave like `private` (share-by-link)', async () => {
143147
await boot([{ id: 'proj_a', organization_id: 'org_1', visibility: 'unlisted' }]);
144148

145149
const enumAttempt = await server.invoke('GET', '/api/v1/pub/v1/projects/proj_a/artifact');
@@ -151,7 +155,7 @@ describe('public artifact API (/pub/v1/projects/:id/*)', () => {
151155
expect(ok.body.data.commitId).toBe('cafebabe');
152156
});
153157

154-
it('unlisted project: revisions and manifest are still hidden', async () => {
158+
it('legacy `unlisted` rows still hide /revisions and /manifest.json', async () => {
155159
await boot([{ id: 'proj_a', organization_id: 'org_1', visibility: 'unlisted' }]);
156160
const revs = await server.invoke('GET', '/api/v1/pub/v1/projects/proj_a/revisions');
157161
const mani = await server.invoke('GET', '/api/v1/pub/v1/projects/proj_a/manifest.json');

packages/services/service-tenant/src/project-provisioning.test.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@ describe('ProjectProvisioningService.provisionProject', () => {
6969
});
7070

7171
const objects = created.map((c) => c.object);
72-
expect(objects).toEqual(['sys_project', 'sys_project_credential']);
72+
expect(objects).toEqual([
73+
'sys_project',
74+
'sys_project_credential',
75+
'sys_project_member',
76+
]);
7377

7478
const projectRow = created.find((c) => c.object === 'sys_project')!.data;
7579
expect(projectRow.organization_id).toBe('org-42');
@@ -83,6 +87,15 @@ describe('ProjectProvisioningService.provisionProject', () => {
8387
expect(projectRow.project_type).toBeUndefined();
8488
expect(projectRow.region).toBeUndefined();
8589

90+
// Creator should be seeded as the initial owner so /projects/:id/members
91+
// returns at least one row immediately after provisioning.
92+
const memberRow = created.find((c) => c.object === 'sys_project_member')!.data;
93+
expect(memberRow.project_id).toBe(projectRow.id);
94+
expect(memberRow.user_id).toBe('user-1');
95+
expect(memberRow.role).toBe('owner');
96+
expect(memberRow.invited_by).toBe('user-1');
97+
expect(memberRow.organization_id).toBe('org-42');
98+
8699
expect(result.warnings).toBeUndefined();
87100
});
88101

0 commit comments

Comments
 (0)