From 926192d3f1e423974b0e9fcb885c55ff8482b229 Mon Sep 17 00:00:00 2001 From: I548646 Date: Thu, 25 Sep 2025 16:40:24 +0200 Subject: [PATCH 1/3] test: add reproduction test --- test/personal-data/crud.test.js | 19 +++++++++++++++++++ test/personal-data/srv/crud-service.cds | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/test/personal-data/crud.test.js b/test/personal-data/crud.test.js index f2778ab..0b83e3c 100644 --- a/test/personal-data/crud.test.js +++ b/test/personal-data/crud.test.js @@ -1817,6 +1817,25 @@ describe('personal data audit logging in CRUD', () => { expect(_logs).toContainMatchObject({ attributes: [{ name: 'notes', new: '***' }] }) expect(_logs).toContainMatchObject({ attributes: [{ name: 'notes' }] }) }) + + test("create a comment where DataSubjectID is an association to Customer with no customer referenced", async () => { + await POST("/crud-1/Comments", { text: "foo" }, { auth: ALICE }); + expect(_logs.length).toBe(0); + }); + + test("create and update a comment where DataSubjectID is an association to Customer with no customer referenced", async () => { + const res = await POST( "/crud-1/Comments", { text: "foo" }, { auth: ALICE }); + expect(res.data.ID).toBeDefined(); + await PATCH(`/crud-1/Comments(${res.data.ID})`, { text: "bar" }, { auth: ALICE }); + expect(_logs.length).toBe(0); + }); + + test("create and delete a comment where DataSubjectID from association to Customer with no customer referenced", async () => { + const res = await POST( "/crud-1/Comments", { text: "foo" }, { auth: ALICE }); + expect(res.data.ID).toBeDefined(); + await DELETE(`/crud-1/Comments(${res.data.ID})`, { auth: ALICE }); + expect(_logs.length).toBe(0); + }); }) describe('with renamings', () => { diff --git a/test/personal-data/srv/crud-service.cds b/test/personal-data/srv/crud-service.cds index 5522964..2d17c8b 100644 --- a/test/personal-data/srv/crud-service.cds +++ b/test/personal-data/srv/crud-service.cds @@ -61,6 +61,10 @@ service CRUD_1 { town @PersonalData.IsPotentiallyPersonal; } + annotate Comments with @PersonalData : {EntitySemantics: 'Other'} { + customer @PersonalData.FieldSemantics: 'DataSubjectID'; + } + annotate CustomerStatus with @PersonalData: {EntitySemantics: 'DataSubjectDetails'} { description @PersonalData.IsPotentiallySensitive; todo @PersonalData.IsPotentiallyPersonal; From 9659ae111ccecb16bcec841952d73d5e239126ba Mon Sep 17 00:00:00 2001 From: I548646 Date: Thu, 25 Sep 2025 19:05:12 +0200 Subject: [PATCH 2/3] test: add failing reproduction tests --- test/personal-data/crud.test.js | 34 ++++++++++++------------- test/personal-data/srv/crud-service.cds | 23 ++++++++++++++--- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/test/personal-data/crud.test.js b/test/personal-data/crud.test.js index 0b83e3c..3d096a0 100644 --- a/test/personal-data/crud.test.js +++ b/test/personal-data/crud.test.js @@ -1818,24 +1818,22 @@ describe('personal data audit logging in CRUD', () => { expect(_logs).toContainMatchObject({ attributes: [{ name: 'notes' }] }) }) - test("create a comment where DataSubjectID is an association to Customer with no customer referenced", async () => { - await POST("/crud-1/Comments", { text: "foo" }, { auth: ALICE }); - expect(_logs.length).toBe(0); - }); - - test("create and update a comment where DataSubjectID is an association to Customer with no customer referenced", async () => { - const res = await POST( "/crud-1/Comments", { text: "foo" }, { auth: ALICE }); - expect(res.data.ID).toBeDefined(); - await PATCH(`/crud-1/Comments(${res.data.ID})`, { text: "bar" }, { auth: ALICE }); - expect(_logs.length).toBe(0); - }); - - test("create and delete a comment where DataSubjectID from association to Customer with no customer referenced", async () => { - const res = await POST( "/crud-1/Comments", { text: "foo" }, { auth: ALICE }); - expect(res.data.ID).toBeDefined(); - await DELETE(`/crud-1/Comments(${res.data.ID})`, { auth: ALICE }); - expect(_logs.length).toBe(0); - }); + test('create an entity that references a data subject without setting a value for that reference', async () =>{ + await POST('/crud-6/CustomerPostalAddress', { street: 'street', town: 'town'}, { auth: ALICE }) + expect(_logs.length).toBe(0) + }) + + test('create and update an entity that references a data subject without a set value for that reference', async () => { + await POST('/crud-6/CustomerPostalAddress', { street: 'street', town: 'town'}, { auth: ALICE }) + await PATCH('/crud-6/CustomerPostalAddress', { town: 'new-town'}, { auth: ALICE }) + expect(_logs.length).toBe(0) + }) + + test('create and delete an entity that references a data subject without a set value for that reference', async () => { + await POST('/crud-6/CustomerPostalAddress', { street: 'street', town: 'town'}, { auth: ALICE }) + await DELETE('/crud-6/CustomerPostalAddress', { auth: ALICE }) + expect(_logs.length).toBe(0) + }) }) describe('with renamings', () => { diff --git a/test/personal-data/srv/crud-service.cds b/test/personal-data/srv/crud-service.cds index 2d17c8b..c81e2aa 100644 --- a/test/personal-data/srv/crud-service.cds +++ b/test/personal-data/srv/crud-service.cds @@ -61,10 +61,6 @@ service CRUD_1 { town @PersonalData.IsPotentiallyPersonal; } - annotate Comments with @PersonalData : {EntitySemantics: 'Other'} { - customer @PersonalData.FieldSemantics: 'DataSubjectID'; - } - annotate CustomerStatus with @PersonalData: {EntitySemantics: 'DataSubjectDetails'} { description @PersonalData.IsPotentiallySensitive; todo @PersonalData.IsPotentiallyPersonal; @@ -220,3 +216,22 @@ service CRUD_5 { entity C as projection on db.C; } + +@path : '/crud-6' +@requires: 'admin' +service CRUD_7 { + entity Customers as projection on db.Customers; + entity CustomerPostalAddress as projection on db.CustomerPostalAddress; + + annotate Customers with @PersonalData.EntitySemantics: 'DataSubject' { + ID @PersonalData.FieldSemantics : 'DataSubjectID'; + firstName @PersonalData.IsPotentiallyPersonal; + lastName @PersonalData.IsPotentiallyPersonal; + } + + annotate CustomerPostalAddress with @PersonalData.EntitySemantics: 'Other' { + customer @PersonalData.FieldSemantics : 'DataSubjectID'; + street @PersonalData.IsPotentiallyPersonal; + town @PersonalData.IsPotentiallyPersonal; + } +} From f0af1fdc30ebf0d92078eddc7da24494948eb2a6 Mon Sep 17 00:00:00 2001 From: I548646 Date: Fri, 26 Sep 2025 10:14:54 +0200 Subject: [PATCH 3/3] test: tests fail for the right reason --- test/personal-data/crud.test.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/personal-data/crud.test.js b/test/personal-data/crud.test.js index 3d096a0..2164e80 100644 --- a/test/personal-data/crud.test.js +++ b/test/personal-data/crud.test.js @@ -1824,14 +1824,16 @@ describe('personal data audit logging in CRUD', () => { }) test('create and update an entity that references a data subject without a set value for that reference', async () => { - await POST('/crud-6/CustomerPostalAddress', { street: 'street', town: 'town'}, { auth: ALICE }) - await PATCH('/crud-6/CustomerPostalAddress', { town: 'new-town'}, { auth: ALICE }) + const res = await POST('/crud-6/CustomerPostalAddress', { street: 'street', town: 'town'}, { auth: ALICE }) + expect(res?.data?.ID).toBeDefined() + await PATCH(`/crud-6/CustomerPostalAddress(ID=${res.data.ID})`, { town: 'new-town'}, { auth: ALICE }) expect(_logs.length).toBe(0) }) test('create and delete an entity that references a data subject without a set value for that reference', async () => { - await POST('/crud-6/CustomerPostalAddress', { street: 'street', town: 'town'}, { auth: ALICE }) - await DELETE('/crud-6/CustomerPostalAddress', { auth: ALICE }) + const res = await POST('/crud-6/CustomerPostalAddress', { street: 'street', town: 'town'}, { auth: ALICE }) + expect(res?.data?.ID).toBeDefined() + await DELETE(`/crud-6/CustomerPostalAddress(ID=${res.data.ID})`, { auth: ALICE }) expect(_logs.length).toBe(0) }) })