@lenne.tech/nest-server 11.21.1 → 11.21.3

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.
@@ -5,7 +5,10 @@ const SYSTEM_ROLE_PREFIX = 's_';
5
5
 
6
6
  /**
7
7
  * Merge handler-level and class-level @Roles() metadata arrays into a single flat array.
8
- * Used by RolesGuard, BetterAuthRolesGuard, and CoreTenantGuard to avoid code duplication.
8
+ * Used by RolesGuard, BetterAuthRolesGuard, and CoreTenantGuard.
9
+ *
10
+ * OR semantics: class-level roles serve as a base that method-level roles extend.
11
+ * Example: class @Roles(ADMIN) + method @Roles(S_USER) → [S_USER, ADMIN] — both are alternatives.
9
12
  *
10
13
  * @param meta - Two-element tuple [handlerRoles, classRoles] from Reflector.getAll or Reflect.getMetadata
11
14
  */
@@ -22,7 +25,8 @@ export function getRoleHierarchy(): Record<string, number> {
22
25
 
23
26
  /**
24
27
  * Check if a role is a system role (S_USER, S_EVERYONE, etc.).
25
- * System roles are handled by RolesGuard, not CoreTenantGuard.
28
+ * System roles are checked by RolesGuard/BetterAuthRolesGuard for authentication
29
+ * and by CoreTenantGuard as OR alternatives before real role checks.
26
30
  */
27
31
  export function isSystemRole(role: string): boolean {
28
32
  return role.startsWith(SYSTEM_ROLE_PREFIX);
@@ -174,6 +174,53 @@ await testHelper.rest('/protected-endpoint', {
174
174
  });
175
175
  ```
176
176
 
177
+ ## File Download Testing (`testHelper.download()` / `testHelper.downloadBuffer()`)
178
+
179
+ ### `download(url, tokenOrOptions?)`
180
+
181
+ Download a file and return the response with a `data` string property for content comparison.
182
+
183
+ ```typescript
184
+ // No authentication
185
+ const res = await testHelper.download('/files/id/abc123');
186
+ expect(res.statusCode).toEqual(200);
187
+ expect(res.data).toEqual('file content');
188
+ ```
189
+
190
+ ### `downloadBuffer(url, tokenOrOptions?)`
191
+
192
+ Download a file and return a `Buffer` for binary comparison or saving.
193
+
194
+ ```typescript
195
+ const buffer = await testHelper.downloadBuffer('/files/id/abc123', jwtToken);
196
+ await fs.promises.writeFile('/tmp/downloaded.bin', buffer);
197
+ ```
198
+
199
+ ### TestDownloadOptions
200
+
201
+ The second parameter accepts either a plain token string or a `TestDownloadOptions` object:
202
+
203
+ | Option | Type | Description |
204
+ | --------- | -------- | ------------------------------------------------------------- |
205
+ | `token` | `string` | Bearer token via Authorization header (JWT) |
206
+ | `cookies` | `string` | Plain session token, converted via `buildBetterAuthCookies()` |
207
+
208
+ Both can be used simultaneously — `token` sets the Authorization header while `cookies` sets the Cookie header.
209
+
210
+ ```typescript
211
+ // String form (backward compatible) — sets Authorization: bearer <token>
212
+ await testHelper.download('/files/id/abc123', jwtToken);
213
+
214
+ // Options object with JWT token
215
+ await testHelper.download('/files/id/abc123', { token: jwtToken });
216
+
217
+ // Options object with cookie-based session auth
218
+ await testHelper.download('/files/id/abc123', { cookies: sessionToken });
219
+
220
+ // Both simultaneously
221
+ await testHelper.download('/files/id/abc123', { cookies: sessionToken, token: jwtToken });
222
+ ```
223
+
177
224
  ## GraphQL Testing (`testHelper.graphQl()`)
178
225
 
179
226
  ```typescript
@@ -154,6 +154,25 @@ export interface TestRestOptions {
154
154
  token?: string;
155
155
  }
156
156
 
157
+ /**
158
+ * Options for download/downloadBuffer requests
159
+ */
160
+ export interface TestDownloadOptions {
161
+ /**
162
+ * Cookie-based authentication. Pass a plain session token string
163
+ * which is converted via buildBetterAuthCookies() to all relevant cookie names
164
+ * (iam.session_token, token).
165
+ */
166
+ cookies?: string;
167
+
168
+ /**
169
+ * Bearer token via Authorization header (JWT authentication).
170
+ * Can be used simultaneously with `cookies` — token sets the Authorization header
171
+ * while cookies sets the Cookie header.
172
+ */
173
+ token?: string;
174
+ }
175
+
157
176
  /**
158
177
  * Test helper
159
178
  */
@@ -176,13 +195,28 @@ export class TestHelper {
176
195
  /**
177
196
  * Download file from URL
178
197
  * To compare content data via string comparison
198
+ * @param url - URL to download from
199
+ * @param tokenOrOptions - Bearer token string, or {@link TestDownloadOptions} with `cookies` and/or `token`
179
200
  * @return Superagent response with additional data field containing the content of the file
180
201
  */
181
- download(url: string, token?: string): Promise<any> {
202
+ download(url: string, tokenOrOptions?: string | TestDownloadOptions): Promise<any> {
182
203
  return new Promise((resolve, reject) => {
183
204
  const request = supertest((this.app as INestApplication).getHttpServer()).get(url);
184
- if (token) {
185
- request.set('Authorization', `bearer ${token}`);
205
+ if (typeof tokenOrOptions === 'string') {
206
+ request.set('Authorization', `bearer ${tokenOrOptions}`);
207
+ } else if (tokenOrOptions) {
208
+ if (tokenOrOptions.cookies) {
209
+ const cookieRecord = TestHelper.buildBetterAuthCookies(tokenOrOptions.cookies);
210
+ request.set(
211
+ 'Cookie',
212
+ Object.entries(cookieRecord)
213
+ .map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
214
+ .join('; '),
215
+ );
216
+ }
217
+ if (tokenOrOptions.token) {
218
+ request.set('Authorization', `bearer ${tokenOrOptions.token}`);
219
+ }
186
220
  }
187
221
  let data = '';
188
222
  request
@@ -207,12 +241,27 @@ export class TestHelper {
207
241
  /**
208
242
  * Download file from URL and get buffer
209
243
  * To compare content data via buffer comparison and with the possibility to save the file
244
+ * @param url - URL to download from
245
+ * @param tokenOrOptions - Bearer token string, or {@link TestDownloadOptions} with `cookies` and/or `token`
210
246
  */
211
- downloadBuffer(url: string, token?: string): Promise<Buffer> {
247
+ downloadBuffer(url: string, tokenOrOptions?: string | TestDownloadOptions): Promise<Buffer> {
212
248
  return new Promise((resolve, reject) => {
213
249
  const request = supertest(this.app.getHttpServer()).get(url);
214
- if (token) {
215
- request.set('Authorization', `bearer ${token}`);
250
+ if (typeof tokenOrOptions === 'string') {
251
+ request.set('Authorization', `bearer ${tokenOrOptions}`);
252
+ } else if (tokenOrOptions) {
253
+ if (tokenOrOptions.cookies) {
254
+ const cookieRecord = TestHelper.buildBetterAuthCookies(tokenOrOptions.cookies);
255
+ request.set(
256
+ 'Cookie',
257
+ Object.entries(cookieRecord)
258
+ .map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
259
+ .join('; '),
260
+ );
261
+ }
262
+ if (tokenOrOptions.token) {
263
+ request.set('Authorization', `bearer ${tokenOrOptions.token}`);
264
+ }
216
265
  }
217
266
 
218
267
  // Array to store the data chunks