Skip to content

Commit b498911

Browse files
committed
test(json-api-server-e2e): improve test descriptions and add comprehensive documentation for JSON-RPC, ACL, and JSON API functionalities
1 parent e39e8d7 commit b498911

15 files changed

+406
-140
lines changed

apps/json-api-server-e2e/src/json-api/json-acl/1-get-all-acl-check.spec.ts

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
/**
2+
* ACL: GET All Resources - Permission and Field-Level Security
3+
*
4+
* This test suite verifies ACL (Access Control List) enforcement for fetching collections
5+
* of resources. It tests three permission levels with different capabilities:
6+
*
7+
* 1. Admin Role: Full access without conditions
8+
* - Can read all resources and all fields
9+
* - Can include all relationships
10+
*
11+
* 2. Moderator Role: Full access with field restrictions
12+
* - Can read all resources
13+
* - Cannot read sensitive fields (salary, role in nested relations)
14+
* - Field filtering applied automatically by ACL
15+
*
16+
* 3. User Role: Conditional access with field restrictions
17+
* - Can read only public profiles OR their own profile
18+
* - Cannot read sensitive fields (salary, role, isPublic, createdAt, updatedAt)
19+
* - Can read own phone number but not others' phone numbers
20+
* - Row-level filtering applied automatically by ACL
21+
*/
22+
123
import {
224
ContextTestAcl,
325
UserRole,
@@ -9,8 +31,7 @@ import { JsonSdkPromise } from '@klerick/json-api-nestjs-sdk';
931
import { creatSdk } from '../utils/run-application';
1032
import { AbilityBuilder, CheckFieldAndInclude } from '../utils/acl/acl';
1133

12-
13-
describe('ACL getAll:', () => {
34+
describe('ACL: GET All Resources (Collection Fetching)', () => {
1435
let contextTestAcl = new ContextTestAcl();
1536
let usersAcl: UsersAcl[];
1637
contextTestAcl.aclRules = { rules: [] };
@@ -28,7 +49,7 @@ describe('ACL getAll:', () => {
2849
await jsonSdk.jonApiSdkService.deleteOne(contextTestAcl);
2950
});
3051

31-
describe('Without conditional: admin', () => {
52+
describe('Admin Role: Full Access Without Restrictions', () => {
3253
beforeEach(async () => {
3354
const adminUser = usersAcl.find((user) => user.login === 'admin');
3455
if (!adminUser) throw new Error('Daphne user not found');
@@ -38,18 +59,18 @@ describe('ACL getAll:', () => {
3859
await jsonSdk.jonApiSdkService.patchOne(contextTestAcl);
3960
});
4061

41-
it('get all profile', async () => {
62+
it('should fetch all profiles with all fields (no ACL restrictions)', async () => {
4263
await jsonSdk.jonApiSdkService.getAll(UserProfileAcl)
4364
})
4465

45-
it('get all users with profile', async () => {
66+
it('should fetch all users with included profiles with all fields', async () => {
4667
await jsonSdk.jonApiSdkService.getAll(UsersAcl, {
4768
include: ['profile'],
4869
});
4970
});
5071
})
5172

52-
describe('Without conditional but with fields: moderator', () => {
73+
describe('Moderator Role: Full Access with Field-Level Restrictions', () => {
5374
beforeEach(async () => {
5475
const moderatorUser = usersAcl.find((user) => user.login === 'moderator');
5576
if (!moderatorUser) throw new Error('Sheila user not found');
@@ -59,7 +80,7 @@ describe('ACL getAll:', () => {
5980
await jsonSdk.jonApiSdkService.patchOne(contextTestAcl);
6081
});
6182

62-
it('get all profile', async () => {
83+
it('should fetch all profiles but exclude sensitive fields (role, salary)', async () => {
6384
const result = await jsonSdk.jonApiSdkService.getAll(UserProfileAcl)
6485

6586
for (const item of result) {
@@ -75,7 +96,7 @@ describe('ACL getAll:', () => {
7596
}
7697
})
7798

78-
it('get all users with profile', async () => {
99+
it('should fetch all users with profiles but exclude salary from nested profile', async () => {
79100
const result = await jsonSdk.jonApiSdkService.getAll(UsersAcl, {
80101
include: ['profile'],
81102
});
@@ -87,7 +108,7 @@ describe('ACL getAll:', () => {
87108
});
88109
})
89110

90-
describe('With conditional: user', () => {
111+
describe('User Role: Conditional Row-Level Access with Field Restrictions', () => {
91112
let countPublicProfile: UserProfileAcl[];
92113
beforeEach(async () => {
93114
countPublicProfile = await jsonSdk.jonApiSdkService.getAll(UserProfileAcl, {
@@ -106,7 +127,7 @@ describe('ACL getAll:', () => {
106127
await jsonSdk.jonApiSdkService.patchOne(contextTestAcl);
107128
});
108129

109-
it('should be able to get allow profile', async () => {
130+
it('should fetch only public profiles and own profile, with field restrictions and conditional phone visibility', async () => {
110131

111132
const result = await jsonSdk.jonApiSdkService.getAll(UserProfileAcl);
112133
expect(result.length).toBe(countPublicProfile.length + 1)

apps/json-api-server-e2e/src/json-api/json-acl/10-atomic-operation.spec.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
/**
2+
* ACL: Atomic Operations - ACL Enforcement Across Batch Requests
3+
*
4+
* This test suite verifies ACL enforcement for atomic operations (batch requests)
5+
* where multiple operations are executed together. ACL rules are evaluated for
6+
* EACH individual operation within the atomic request.
7+
*
8+
* 1. Admin Role: Full atomic operation access
9+
* - Can execute multiple operations in one atomic request
10+
* - Can POST, PATCH, and DELETE in a single transaction
11+
* - All operations succeed when permissions allow
12+
*
13+
* 2. Moderator Role: Partial atomic operation access
14+
* - Can POST and PATCH in atomic request
15+
* - CANNOT DELETE (returns 403 Forbidden for entire atomic request)
16+
* - Atomic request fails if ANY operation violates ACL
17+
* - Error message indicates which operation failed (e.g., "deleteOne on ArticleAcl")
18+
* - Atomicity ensures either ALL operations succeed or ALL fail
19+
*/
20+
121
import { faker } from '@faker-js/faker';
222
import {
323
ArticleAcl,
@@ -12,9 +32,6 @@ import { JsonSdkPromise } from '@klerick/json-api-nestjs-sdk';
1232
import { creatSdk } from '../utils/run-application';
1333
import { AbilityBuilder, CheckFieldAndInclude } from '../utils/acl/acl';
1434

15-
16-
17-
1835
const getArticleData = () => ({
1936
title: faker.lorem.sentence(),
2037
content: faker.lorem.paragraphs(8),
@@ -30,7 +47,7 @@ const getArticleData = () => ({
3047
expiresAt: null,
3148
});
3249

33-
describe('ACL atomic operation:', () => {
50+
describe('ACL: Atomic Operations (Batch Request ACL Enforcement)', () => {
3451
let contextTestAcl = new ContextTestAcl();
3552
let usersAcl: UsersAcl[];
3653
let articleAcl: ArticleAcl[];
@@ -51,7 +68,7 @@ describe('ACL atomic operation:', () => {
5168
await jsonSdk.jonApiSdkService.deleteOne(contextTestAcl);
5269
});
5370

54-
describe('Without conditional: admin', () => {
71+
describe('Admin Role: Full Atomic Operation Access', () => {
5572
let bobUser: UsersAcl;
5673
beforeEach(async () => {
5774
const adminUser = usersAcl.find((user) => user.login === 'admin');
@@ -69,7 +86,7 @@ describe('ACL atomic operation:', () => {
6986
await jsonSdk.jonApiSdkService.patchOne(contextTestAcl);
7087
});
7188

72-
it('create one publish article with moderator author', async () => {
89+
it('should execute atomic operation with POST, PATCH, and DELETE all succeeding', async () => {
7390
const articleForCreate = Object.assign(
7491
new ArticleAcl(),
7592
getArticleData(),
@@ -82,7 +99,7 @@ describe('ACL atomic operation:', () => {
8299
});
83100
});
84101

85-
describe('With conditional but with fields: moderator', () => {
102+
describe('Moderator Role: Partial Atomic Operation Access with DELETE Restriction', () => {
86103

87104
let bobUser: UsersAcl;
88105
let moderatorUser: UsersAcl;
@@ -104,7 +121,7 @@ describe('ACL atomic operation:', () => {
104121
await jsonSdk.jonApiSdkService.patchOne(contextTestAcl);
105122
});
106123

107-
it('allow create and patch but delete not allow', async () => {
124+
it('should return 403 Forbidden for entire atomic request when DELETE operation violates ACL (POST and PATCH allowed but DELETE forbidden)', async () => {
108125
const articleForCreate = Object.assign(
109126
new ArticleAcl(),
110127
getArticleData()

apps/json-api-server-e2e/src/json-api/json-acl/2-get-one-acl-check.spec.ts

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
1+
/**
2+
* ACL: GET One Resource - Permission and Field-Level Security
3+
*
4+
* This test suite verifies ACL enforcement for fetching individual resources by ID.
5+
* It tests three permission levels with different capabilities:
6+
*
7+
* 1. Admin Role: Full access without conditions
8+
* - Can read any resource by ID with all fields
9+
* - Can include all relationships
10+
*
11+
* 2. Moderator Role: Full access with field restrictions
12+
* - Can read any resource by ID
13+
* - Cannot read sensitive fields (salary, role in nested relations)
14+
* - Field filtering applied automatically by ACL
15+
*
16+
* 3. User Role: Conditional row-level access with field restrictions
17+
* - Can read own profile with phone number visible
18+
* - Can read public profiles with phone number hidden
19+
* - CANNOT read private profiles (returns 404 Not Found)
20+
* - Cannot read sensitive fields (salary, role, isPublic, createdAt, updatedAt)
21+
* - Row-level filtering prevents access to forbidden resources
22+
*/
23+
124
import {
225
ContextTestAcl,
326
UserProfileAcl,
@@ -10,7 +33,7 @@ import { AxiosError } from 'axios';
1033
import { creatSdk } from '../utils/run-application';
1134
import { AbilityBuilder, CheckFieldAndInclude } from '../utils/acl/acl';
1235

13-
describe('ACL getOne:', () => {
36+
describe('ACL: GET One Resource (Single Resource Fetching)', () => {
1437
let contextTestAcl = new ContextTestAcl();
1538
let usersAcl: UsersAcl[];
1639
contextTestAcl.aclRules = { rules: [] };
@@ -34,7 +57,7 @@ describe('ACL getOne:', () => {
3457
await jsonSdk.jonApiSdkService.deleteOne(contextTestAcl);
3558
});
3659

37-
describe('Without conditional: admin', () => {
60+
describe('Admin Role: Full Access Without Restrictions', () => {
3861
beforeEach(async () => {
3962
const adminUser = usersAcl.find((user) => user.login === 'admin');
4063
if (!adminUser) throw new Error('Daphne user not found');
@@ -46,18 +69,18 @@ describe('ACL getOne:', () => {
4669
await jsonSdk.jonApiSdkService.patchOne(contextTestAcl);
4770
});
4871

49-
it('get one profile', async () => {
72+
it('should fetch any user by ID with all fields (no ACL restrictions)', async () => {
5073
await jsonSdk.jonApiSdkService.getOne(UsersAcl, usersAcl[0].id);
5174
});
5275

53-
it('get one users with profile', async () => {
76+
it('should fetch any user by ID with included profile with all fields', async () => {
5477
await jsonSdk.jonApiSdkService.getOne(UsersAcl, usersAcl[0].id, {
5578
include: ['profile'],
5679
});
5780
});
5881
});
5982

60-
describe('Without conditional but with fields: moderator', () => {
83+
describe('Moderator Role: Full Access with Field-Level Restrictions', () => {
6184
beforeEach(async () => {
6285
const moderatorUser = usersAcl.find((user) => user.login === 'moderator');
6386
if (!moderatorUser) throw new Error('Sheila user not found');
@@ -69,7 +92,7 @@ describe('ACL getOne:', () => {
6992
await jsonSdk.jonApiSdkService.patchOne(contextTestAcl);
7093
});
7194

72-
it('get one profile', async () => {
95+
it('should fetch any profile by ID but exclude sensitive fields (role, salary)', async () => {
7396
const item = await jsonSdk.jonApiSdkService.getOne(
7497
UserProfileAcl,
7598
usersAcl[0].id
@@ -85,7 +108,7 @@ describe('ACL getOne:', () => {
85108
expect(item.updatedAt).toBeDefined();
86109
});
87110

88-
it('get one users with profile', async () => {
111+
it('should fetch any user by ID with profile but exclude salary from nested profile', async () => {
89112
const item = await jsonSdk.jonApiSdkService.getOne(
90113
UsersAcl,
91114
usersAcl[0].id,
@@ -99,7 +122,7 @@ describe('ACL getOne:', () => {
99122
});
100123
});
101124

102-
describe('With conditional: user', () => {
125+
describe('User Role: Conditional Row-Level Access with Field Restrictions', () => {
103126
let bobUser: UsersAcl;
104127
beforeEach(async () => {
105128
const posibleBobUser = usersAcl.find((user) => user.login === 'bob');
@@ -112,7 +135,7 @@ describe('ACL getOne:', () => {
112135
await jsonSdk.jonApiSdkService.patchOne(contextTestAcl);
113136
});
114137

115-
it('should be able to get owner profile', async () => {
138+
it('should fetch own profile with phone visible and sensitive fields excluded', async () => {
116139
const item = await jsonSdk.jonApiSdkService.getOne(
117140
UserProfileAcl,
118141
bobUser.profile.id
@@ -129,7 +152,7 @@ describe('ACL getOne:', () => {
129152
expect(item.bio).toBeDefined();
130153
});
131154

132-
it('should be able to get public profile', async () => {
155+
it('should fetch public profile with phone hidden and sensitive fields excluded', async () => {
133156
const item = await jsonSdk.jonApiSdkService.getOne(
134157
UserProfileAcl,
135158
publicUser.profile.id
@@ -145,7 +168,7 @@ describe('ACL getOne:', () => {
145168
expect(item.avatar).toBeDefined();
146169
expect(item.bio).toBeDefined();
147170
});
148-
it('should be not found to get not public profile', async () => {
171+
it('should return 404 Not Found when attempting to fetch private profile of another user', async () => {
149172
try {
150173
await jsonSdk.jonApiSdkService.getOne(
151174
UserProfileAcl,

0 commit comments

Comments
 (0)