Skip to content

Commit 80e678c

Browse files
committed
fix(V2): move non-home ownership tests to v2 remote sync CI job
The v2 live api tests job has no V2 governance body configured, so no non-home projects are ever synced. Move the ownership guard rejection tests to a curl-based step in the v2 remote sync tests job where governance is active and non-home project data is available.
1 parent a0dd448 commit 80e678c

2 files changed

Lines changed: 62 additions & 87 deletions

File tree

.github/workflows/tests.yaml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4361,6 +4361,68 @@ jobs:
43614361
fi
43624362
echo "All AEF data matches expected values"
43634363
4364+
- name: Verify ownership guards block non-home mutations
4365+
shell: bash
4366+
run: |
4367+
echo "########################################################"
4368+
echo "Verifying ownership guards reject non-home org mutations"
4369+
echo "########################################################"
4370+
BASE="http://127.0.0.1:31310/v2"
4371+
ERRORS=0
4372+
4373+
PROJECT=$(curl -s "$BASE/project?page=1&limit=1" | jq '(if type == "object" then .data else . end)[0]')
4374+
PROJECT_ID=$(echo "$PROJECT" | jq -r '.cadTrustProjectId')
4375+
echo "Using synced project: $PROJECT_ID"
4376+
4377+
echo ""
4378+
echo "--- PUT update on non-home project (expect 400) ---"
4379+
PUT_RESP=$(curl -s -w "\n%{http_code}" -X PUT "$BASE/project/$PROJECT_ID" \
4380+
-H "Content-Type: application/json" \
4381+
-d "{\"projectName\": \"Should Not Update $(date +%s)\"}")
4382+
PUT_HTTP=$(echo "$PUT_RESP" | tail -1)
4383+
PUT_BODY=$(echo "$PUT_RESP" | head -n -1)
4384+
if [ "$PUT_HTTP" = "400" ]; then
4385+
echo " PASS: PUT returned 400"
4386+
if echo "$PUT_BODY" | jq -r '.error' 2>/dev/null | grep -q "Restricted data"; then
4387+
echo " PASS: Error contains 'Restricted data'"
4388+
else
4389+
echo " FAIL: Expected 'Restricted data' in error body"
4390+
echo " Body: $PUT_BODY"
4391+
ERRORS=$((ERRORS + 1))
4392+
fi
4393+
else
4394+
echo " FAIL: Expected HTTP 400, got $PUT_HTTP"
4395+
echo " Body: $PUT_BODY"
4396+
ERRORS=$((ERRORS + 1))
4397+
fi
4398+
4399+
echo ""
4400+
echo "--- DELETE on non-home project (expect 400) ---"
4401+
DEL_RESP=$(curl -s -w "\n%{http_code}" -X DELETE "$BASE/project/$PROJECT_ID")
4402+
DEL_HTTP=$(echo "$DEL_RESP" | tail -1)
4403+
DEL_BODY=$(echo "$DEL_RESP" | head -n -1)
4404+
if [ "$DEL_HTTP" = "400" ]; then
4405+
echo " PASS: DELETE returned 400"
4406+
if echo "$DEL_BODY" | jq -r '.error' 2>/dev/null | grep -q "Restricted data"; then
4407+
echo " PASS: Error contains 'Restricted data'"
4408+
else
4409+
echo " FAIL: Expected 'Restricted data' in error body"
4410+
echo " Body: $DEL_BODY"
4411+
ERRORS=$((ERRORS + 1))
4412+
fi
4413+
else
4414+
echo " FAIL: Expected HTTP 400, got $DEL_HTTP"
4415+
echo " Body: $DEL_BODY"
4416+
ERRORS=$((ERRORS + 1))
4417+
fi
4418+
4419+
echo ""
4420+
if [ "$ERRORS" -gt 0 ]; then
4421+
echo "FAILED: $ERRORS ownership guard checks failed"
4422+
exit 1
4423+
fi
4424+
echo "All ownership guard checks passed"
4425+
43644426
- name: Show CADT logs after tests
43654427
if: always()
43664428
shell: bash

tests/v2/live-api/project-validation.live.spec.js

Lines changed: 0 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -25,53 +25,6 @@ import {
2525
getInvalidPicklistValue,
2626
} from './data/test-data-generators.js';
2727

28-
const findNonHomeProject = async (request, homeOrgId) => {
29-
let page = 1;
30-
const limit = 100;
31-
32-
while (page <= 10) {
33-
const response = await request
34-
.get('/v2/project')
35-
.query({ page, limit })
36-
.expect(200);
37-
const data = Array.isArray(response.body) ? response.body : (response.body?.data || []);
38-
const nonHomeProject = data.find(record => record.orgUid && record.orgUid !== homeOrgId);
39-
if (nonHomeProject) return nonHomeProject;
40-
41-
const totalPages = response.body?.pageCount || 1;
42-
if (page >= totalPages || data.length < limit) break;
43-
page++;
44-
}
45-
46-
return null;
47-
};
48-
49-
const requireNonHomeProject = async (request, homeOrgId, mochaContext) => {
50-
const nonHomeProject = await findNonHomeProject(request, homeOrgId);
51-
if (!nonHomeProject) {
52-
mochaContext.skip();
53-
}
54-
return nonHomeProject;
55-
};
56-
57-
const buildProjectUpdateData = (record, overrides = {}) => ({
58-
projectRegistryName: record.projectRegistryName,
59-
projectId: record.projectId,
60-
projectName: record.projectName,
61-
projectCreditingProgram: record.projectCreditingProgram ?? null,
62-
projectLink: record.projectLink ?? null,
63-
projectDescription: record.projectDescription ?? null,
64-
projectSector: record.projectSector ?? null,
65-
projectType: record.projectType ?? null,
66-
projectSubtype: record.projectSubtype ?? null,
67-
projectStatus: record.projectStatus ?? null,
68-
projectStatusDate: record.projectStatusDate ?? null,
69-
projectUnitMetric: record.projectUnitMetric ?? null,
70-
cadTrustReferenceProjectId: record.cadTrustReferenceProjectId ?? null,
71-
cadTrustProgramId: record.cadTrustProgramId ?? null,
72-
...overrides,
73-
});
74-
7528
describe('Project Live API Validation Tests', function () {
7629
this.timeout(600000); // 10 minute timeout
7730
let request;
@@ -225,26 +178,6 @@ describe('Project Live API Validation Tests', function () {
225178
});
226179
});
227180
describe('Step 7: PUT Request Tests', function () {
228-
it('should reject updating a project not owned by the home organization', async function () {
229-
const nonHomeProject = await requireNonHomeProject(request, homeOrgId, this);
230-
231-
const updateData = buildProjectUpdateData(nonHomeProject, {
232-
projectName: `Should Not Update ${Date.now()}`,
233-
});
234-
235-
try {
236-
const response = await request
237-
.put(`/v2/project/${nonHomeProject.cadTrustProjectId}`)
238-
.send(updateData);
239-
240-
expect(response.status).to.equal(400);
241-
expect(response.body.success).to.be.false;
242-
expect(response.body.error).to.include('Restricted data');
243-
} finally {
244-
await clearStagingTable(request);
245-
}
246-
});
247-
248181
it('should update a project', async function () {
249182
// Get ID from createdIds (if available) or query for test records we created
250183
let id = createdIds[0];
@@ -358,11 +291,6 @@ describe('Project Live API Validation Tests', function () {
358291
}
359292
});
360293

361-
it('should include synced project data from another organization', async function () {
362-
const nonHomeProject = await requireNonHomeProject(request, homeOrgId, this);
363-
expect(nonHomeProject.orgUid).to.not.equal(homeOrgId);
364-
});
365-
366294
it('should support search functionality', async function () {
367295
// Test search if supported by endpoint
368296
const response = await request
@@ -374,21 +302,6 @@ describe('Project Live API Validation Tests', function () {
374302
});
375303
});
376304
describe('Step 9: DELETE Request Tests', function () {
377-
it('should reject deleting a project not owned by the home organization', async function () {
378-
const nonHomeProject = await requireNonHomeProject(request, homeOrgId, this);
379-
380-
try {
381-
const response = await request
382-
.delete(`/v2/project/${nonHomeProject.cadTrustProjectId}`);
383-
384-
expect(response.status).to.equal(400);
385-
expect(response.body.success).to.be.false;
386-
expect(response.body.error).to.include('Restricted data');
387-
} finally {
388-
await clearStagingTable(request);
389-
}
390-
});
391-
392305
it('should delete all created projects', async function () {
393306
// Query for test projects by orgUid and TEST- prefix
394307
// This works even when DELETE runs in a separate process

0 commit comments

Comments
 (0)