Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fd0dc7528d | |||
| 33b122d22b | |||
| c4e584cc3b | |||
| 35ff21a810 |
@@ -6,6 +6,10 @@ on:
|
|||||||
- 'release/**'
|
- 'release/**'
|
||||||
- 'develop*'
|
- 'develop*'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -2,9 +2,13 @@ name: CI
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["**"]
|
branches: ["**", "!release/**"]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: ["**"]
|
branches: ["**", "!release/**"]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
audit:
|
audit:
|
||||||
|
|||||||
@@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file.
|
|||||||
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.7.25] - 2026-05-27
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Ombi TV Request Status, User, and Date Resolution (Issue #53)** — Resolved the root cause where Ombi TV show requests consistently displayed "unknown" status, "unknown" user, and missing request dates. The Ombi API nests all TV request data (`requestedUser`, `approved`, `available`, `denied`, `requested`, `requestedDate`) inside `childRequests[]` sub-objects, while the application previously only inspected top-level properties.
|
||||||
|
- `OmbiRetriever._hydrateRequest()` now hydrates `requestedUser` on each `childRequests` entry and promotes `requestedDate` from `childRequests[0]` to the top level.
|
||||||
|
- `getRequestStatus()` (server and client) now aggregates status flags from `childRequests[]` when top-level properties are absent.
|
||||||
|
- Client-side date display now falls back to `childRequests[0].requestedDate` as a defensive measure.
|
||||||
|
|
||||||
|
## [1.7.24] - 2026-05-27
|
||||||
|
|
||||||
|
### Enhanced
|
||||||
|
|
||||||
|
- **Gitea Actions Prioritization** — Optimized CI/CD workflow pipeline executions to prioritize critical `build-image` Docker compilation runs. Redundant security audits, tests, coverage generation, and RAML packages are now bypassed on `release/**` branches (which have already passed validation during development on `develop`).
|
||||||
|
- **Workflow Concurrency Controls** — Configured active concurrency groups with `cancel-in-progress: true` inside both `ci.yml` and `build-image.yml` pipelines, ensuring obsolete running jobs are aborted instantly when newer commits are pushed.
|
||||||
|
|
||||||
## [1.7.23] - 2026-05-27
|
## [1.7.23] - 2026-05-27
|
||||||
|
|
||||||
### Enhanced
|
### Enhanced
|
||||||
|
|||||||
@@ -158,7 +158,8 @@ function createRequestCard(request) {
|
|||||||
}
|
}
|
||||||
meta.appendChild(user);
|
meta.appendChild(user);
|
||||||
|
|
||||||
const dateStr = request.requestedDate || request.RequestedDate || request.date || request.Date;
|
const childDate = request.childRequests && request.childRequests[0] ? (request.childRequests[0].requestedDate || request.childRequests[0].RequestedDate) : null;
|
||||||
|
const dateStr = request.requestedDate || request.RequestedDate || request.date || request.Date || childDate;
|
||||||
if (dateStr) {
|
if (dateStr) {
|
||||||
const requestDate = document.createElement('span');
|
const requestDate = document.createElement('span');
|
||||||
requestDate.className = 'request-date';
|
requestDate.className = 'request-date';
|
||||||
|
|||||||
@@ -17,6 +17,23 @@ export function getRequestStatus(request) {
|
|||||||
if (request.denied) return 'denied';
|
if (request.denied) return 'denied';
|
||||||
if (request.approved) return 'approved';
|
if (request.approved) return 'approved';
|
||||||
if (request.requested) return 'pending';
|
if (request.requested) return 'pending';
|
||||||
|
|
||||||
|
// Ombi TV requests store status flags inside childRequests
|
||||||
|
if (Array.isArray(request.childRequests) && request.childRequests.length > 0) {
|
||||||
|
for (const child of request.childRequests) {
|
||||||
|
if (child && child.available) return 'available';
|
||||||
|
}
|
||||||
|
for (const child of request.childRequests) {
|
||||||
|
if (child && child.denied) return 'denied';
|
||||||
|
}
|
||||||
|
for (const child of request.childRequests) {
|
||||||
|
if (child && child.approved) return 'approved';
|
||||||
|
}
|
||||||
|
for (const child of request.childRequests) {
|
||||||
|
if (child && child.requested) return 'pending';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 'unknown';
|
return 'unknown';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "sofarr",
|
"name": "sofarr",
|
||||||
"version": "1.7.23",
|
"version": "1.7.25",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "sofarr",
|
"name": "sofarr",
|
||||||
"version": "1.7.23",
|
"version": "1.7.25",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.6.0",
|
"axios": "^1.6.0",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sofarr",
|
"name": "sofarr",
|
||||||
"version": "1.7.23",
|
"version": "1.7.25",
|
||||||
"description": "A personal media download dashboard that shows your downloads 'so far' while you relax on the sofa waiting for your *arr services to finish",
|
"description": "A personal media download dashboard that shows your downloads 'so far' while you relax on the sofa waiting for your *arr services to finish",
|
||||||
"main": "server/index.js",
|
"main": "server/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+1
-1
@@ -132,7 +132,7 @@ function createApp({ skipRateLimits = false } = {}) {
|
|||||||
* version:
|
* version:
|
||||||
* type: string
|
* type: string
|
||||||
* description: sofarr version
|
* description: sofarr version
|
||||||
* example: "1.7.23"
|
* example: "1.7.25"
|
||||||
* x-code-samples:
|
* x-code-samples:
|
||||||
* - lang: curl
|
* - lang: curl
|
||||||
* label: cURL
|
* label: cURL
|
||||||
|
|||||||
@@ -163,6 +163,8 @@ class OmbiRetriever extends ArrRetriever {
|
|||||||
_hydrateRequest(req) {
|
_hydrateRequest(req) {
|
||||||
if (!req) return req;
|
if (!req) return req;
|
||||||
|
|
||||||
|
let result = req;
|
||||||
|
|
||||||
const reqUserId = req.requestedUserId || req.RequestedUserId;
|
const reqUserId = req.requestedUserId || req.RequestedUserId;
|
||||||
if (reqUserId && this.cache.userMap.has(reqUserId)) {
|
if (reqUserId && this.cache.userMap.has(reqUserId)) {
|
||||||
const cachedUser = this.cache.userMap.get(reqUserId);
|
const cachedUser = this.cache.userMap.get(reqUserId);
|
||||||
@@ -179,14 +181,55 @@ class OmbiRetriever extends ArrRetriever {
|
|||||||
normalizedUserName: cachedUser.normalizedUserName || cachedUser.NormalizedUserName || ''
|
normalizedUserName: cachedUser.normalizedUserName || cachedUser.NormalizedUserName || ''
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
result = {
|
||||||
...req,
|
...req,
|
||||||
requestedUser: hydratedUser,
|
requestedUser: hydratedUser,
|
||||||
RequestedUser: hydratedUser
|
RequestedUser: hydratedUser
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return req;
|
|
||||||
|
// Hydrate childRequests (common for Ombi TV show requests)
|
||||||
|
if (Array.isArray(result.childRequests) && result.childRequests.length > 0) {
|
||||||
|
const hydratedChildren = result.childRequests.map(child => {
|
||||||
|
if (!child) return child;
|
||||||
|
|
||||||
|
const childUserId = child.requestedUserId || child.RequestedUserId;
|
||||||
|
if (childUserId && this.cache.userMap.has(childUserId)) {
|
||||||
|
const cachedUser = this.cache.userMap.get(childUserId);
|
||||||
|
let childUser = child.requestedUser || child.RequestedUser;
|
||||||
|
|
||||||
|
if (!childUser || typeof childUser !== 'object' || Object.keys(childUser).length === 0) {
|
||||||
|
const hydratedUser = {
|
||||||
|
id: cachedUser.id,
|
||||||
|
userName: cachedUser.userName,
|
||||||
|
alias: cachedUser.alias || cachedUser.Alias || '',
|
||||||
|
userAlias: cachedUser.userAlias || cachedUser.UserAlias || '',
|
||||||
|
normalizedUserName: cachedUser.normalizedUserName || cachedUser.NormalizedUserName || ''
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...child,
|
||||||
|
requestedUser: hydratedUser,
|
||||||
|
RequestedUser: hydratedUser
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return child;
|
||||||
|
});
|
||||||
|
|
||||||
|
result = { ...result, childRequests: hydratedChildren };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Promote requestedDate from childRequests to top level (common for Ombi TV)
|
||||||
|
if (!result.requestedDate && Array.isArray(result.childRequests) && result.childRequests.length > 0) {
|
||||||
|
const childDate = result.childRequests[0].requestedDate || result.childRequests[0].RequestedDate;
|
||||||
|
if (childDate) {
|
||||||
|
result = { ...result, requestedDate: childDate };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+1
-1
@@ -249,7 +249,7 @@ app.use(express.json({ limit: '64kb' })); // prevent oversized JSON payloads
|
|||||||
* version:
|
* version:
|
||||||
* type: string
|
* type: string
|
||||||
* description: sofarr version
|
* description: sofarr version
|
||||||
* example: "1.7.23"
|
* example: "1.7.25"
|
||||||
*/
|
*/
|
||||||
app.get('/health', (req, res) => {
|
app.get('/health', (req, res) => {
|
||||||
res.json({ status: 'ok', uptime: process.uptime(), version });
|
res.json({ status: 'ok', uptime: process.uptime(), version });
|
||||||
|
|||||||
+1
-1
@@ -22,7 +22,7 @@ info:
|
|||||||
|
|
||||||
## SSE Streaming
|
## SSE Streaming
|
||||||
Real-time updates are available via Server-Sent Events at GET /api/dashboard/stream.
|
Real-time updates are available via Server-Sent Events at GET /api/dashboard/stream.
|
||||||
version: 1.7.23
|
version: 1.7.24
|
||||||
contact:
|
contact:
|
||||||
name: sofarr
|
name: sofarr
|
||||||
license:
|
license:
|
||||||
|
|||||||
@@ -17,6 +17,23 @@ function getRequestStatus(request) {
|
|||||||
if (request.denied) return 'denied';
|
if (request.denied) return 'denied';
|
||||||
if (request.approved) return 'approved';
|
if (request.approved) return 'approved';
|
||||||
if (request.requested) return 'pending';
|
if (request.requested) return 'pending';
|
||||||
|
|
||||||
|
// Ombi TV requests store status flags inside childRequests
|
||||||
|
if (Array.isArray(request.childRequests) && request.childRequests.length > 0) {
|
||||||
|
for (const child of request.childRequests) {
|
||||||
|
if (child && child.available) return 'available';
|
||||||
|
}
|
||||||
|
for (const child of request.childRequests) {
|
||||||
|
if (child && child.denied) return 'denied';
|
||||||
|
}
|
||||||
|
for (const child of request.childRequests) {
|
||||||
|
if (child && child.approved) return 'approved';
|
||||||
|
}
|
||||||
|
for (const child of request.childRequests) {
|
||||||
|
if (child && child.requested) return 'pending';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 'unknown';
|
return 'unknown';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -183,3 +183,152 @@ describe('arrRetrieverRegistry', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('OmbiRetriever._hydrateRequest', () => {
|
||||||
|
let retriever;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
retriever = new OmbiRetriever({
|
||||||
|
id: 'ombi-test',
|
||||||
|
name: 'Test Ombi',
|
||||||
|
url: 'http://localhost:5000',
|
||||||
|
apiKey: 'test-key'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Seed the userMap cache
|
||||||
|
retriever.cache.userMap.set('user-1', {
|
||||||
|
id: 'user-1',
|
||||||
|
userName: 'testuser',
|
||||||
|
alias: 'TestUser',
|
||||||
|
userAlias: 'TestUser',
|
||||||
|
normalizedUserName: 'testuser'
|
||||||
|
});
|
||||||
|
retriever.cache.userMap.set('user-2', {
|
||||||
|
id: 'user-2',
|
||||||
|
userName: 'adminuser',
|
||||||
|
alias: 'AdminUser',
|
||||||
|
userAlias: 'AdminUser',
|
||||||
|
normalizedUserName: 'adminuser'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('hydrates top-level requestedUserId', () => {
|
||||||
|
const req = {
|
||||||
|
id: 1,
|
||||||
|
requestedUserId: 'user-1',
|
||||||
|
requestedUser: {}
|
||||||
|
};
|
||||||
|
const result = retriever._hydrateRequest(req);
|
||||||
|
expect(result.requestedUser.userName).toBe('testuser');
|
||||||
|
expect(result.requestedUser.alias).toBe('TestUser');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('hydrates childRequests requestedUserId (TV requests)', () => {
|
||||||
|
const req = {
|
||||||
|
id: 3,
|
||||||
|
title: 'Test Show',
|
||||||
|
requestedUserId: 'user-1',
|
||||||
|
requestedUser: {},
|
||||||
|
childRequests: [
|
||||||
|
{
|
||||||
|
id: 10,
|
||||||
|
requestedUserId: 'user-2',
|
||||||
|
requestedUser: {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const result = retriever._hydrateRequest(req);
|
||||||
|
expect(result.requestedUser.userName).toBe('testuser');
|
||||||
|
expect(result.childRequests[0].requestedUser.userName).toBe('adminuser');
|
||||||
|
expect(result.childRequests[0].requestedUser.alias).toBe('AdminUser');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('promotes requestedDate from childRequests to top level', () => {
|
||||||
|
const req = {
|
||||||
|
id: 3,
|
||||||
|
title: 'Test Show',
|
||||||
|
childRequests: [
|
||||||
|
{
|
||||||
|
id: 10,
|
||||||
|
requestedDate: '2026-05-15T10:00:00.000Z'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const result = retriever._hydrateRequest(req);
|
||||||
|
expect(result.requestedDate).toBe('2026-05-15T10:00:00.000Z');
|
||||||
|
expect(result.childRequests[0].requestedDate).toBe('2026-05-15T10:00:00.000Z');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not overwrite existing top-level requestedDate', () => {
|
||||||
|
const req = {
|
||||||
|
id: 3,
|
||||||
|
requestedDate: '2026-01-01T00:00:00.000Z',
|
||||||
|
childRequests: [
|
||||||
|
{
|
||||||
|
id: 10,
|
||||||
|
requestedDate: '2026-05-15T10:00:00.000Z'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const result = retriever._hydrateRequest(req);
|
||||||
|
expect(result.requestedDate).toBe('2026-01-01T00:00:00.000Z');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles PascalCase RequestedDate from childRequests', () => {
|
||||||
|
const req = {
|
||||||
|
id: 3,
|
||||||
|
childRequests: [
|
||||||
|
{
|
||||||
|
id: 10,
|
||||||
|
RequestedDate: '2026-06-01T12:00:00.000Z'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const result = retriever._hydrateRequest(req);
|
||||||
|
expect(result.requestedDate).toBe('2026-06-01T12:00:00.000Z');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns unmodified request when no hydration needed', () => {
|
||||||
|
const req = {
|
||||||
|
id: 1,
|
||||||
|
title: 'Test Movie',
|
||||||
|
requestedUser: { userName: 'existing', alias: 'Existing' }
|
||||||
|
};
|
||||||
|
const result = retriever._hydrateRequest(req);
|
||||||
|
expect(result).toEqual(req);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles null childRequests gracefully', () => {
|
||||||
|
const req = {
|
||||||
|
id: 3,
|
||||||
|
childRequests: null
|
||||||
|
};
|
||||||
|
const result = retriever._hydrateRequest(req);
|
||||||
|
expect(result).toEqual(req);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles empty childRequests gracefully', () => {
|
||||||
|
const req = {
|
||||||
|
id: 3,
|
||||||
|
childRequests: []
|
||||||
|
};
|
||||||
|
const result = retriever._hydrateRequest(req);
|
||||||
|
expect(result).toEqual(req);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skips child hydration when child already has valid requestedUser', () => {
|
||||||
|
const req = {
|
||||||
|
id: 3,
|
||||||
|
childRequests: [
|
||||||
|
{
|
||||||
|
id: 10,
|
||||||
|
requestedUserId: 'user-1',
|
||||||
|
requestedUser: { userName: 'already_set', alias: 'AlreadySet' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const result = retriever._hydrateRequest(req);
|
||||||
|
expect(result.childRequests[0].requestedUser.userName).toBe('already_set');
|
||||||
|
expect(result.childRequests[0].requestedUser.alias).toBe('AlreadySet');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -58,6 +58,40 @@ describe('getRequestStatus', () => {
|
|||||||
expect(getRequestStatus(makeRequest({ denied: true, approved: true }))).toBe('denied');
|
expect(getRequestStatus(makeRequest({ denied: true, approved: true }))).toBe('denied');
|
||||||
expect(getRequestStatus(makeRequest({ approved: true, requested: true }))).toBe('approved');
|
expect(getRequestStatus(makeRequest({ approved: true, requested: true }))).toBe('approved');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns available from childRequests when top-level is absent (TV)', () => {
|
||||||
|
expect(getRequestStatus({ childRequests: [{ available: true }] })).toBe('available');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns denied from childRequests when top-level is absent (TV)', () => {
|
||||||
|
expect(getRequestStatus({ childRequests: [{ denied: true }] })).toBe('denied');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns approved from childRequests when top-level is absent (TV)', () => {
|
||||||
|
expect(getRequestStatus({ childRequests: [{ approved: true }] })).toBe('approved');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns pending from childRequests when top-level is absent (TV)', () => {
|
||||||
|
expect(getRequestStatus({ childRequests: [{ requested: true }] })).toBe('pending');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('follows priority inside childRequests: available > denied > approved > pending', () => {
|
||||||
|
expect(getRequestStatus({ childRequests: [
|
||||||
|
{ available: true, denied: true },
|
||||||
|
{ approved: true }
|
||||||
|
]})).toBe('available');
|
||||||
|
expect(getRequestStatus({ childRequests: [
|
||||||
|
{ denied: true, approved: true },
|
||||||
|
{ requested: true }
|
||||||
|
]})).toBe('denied');
|
||||||
|
expect(getRequestStatus({ childRequests: [
|
||||||
|
{ approved: true, requested: true }
|
||||||
|
]})).toBe('approved');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns unknown for TV request with empty childRequests', () => {
|
||||||
|
expect(getRequestStatus({ childRequests: [] })).toBe('unknown');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -130,6 +130,34 @@ describe('ombiHelpers', () => {
|
|||||||
};
|
};
|
||||||
expect(extractRequestedUser(req)).toBe('child_user');
|
expect(extractRequestedUser(req)).toBe('child_user');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('recursively extracts user from childRequests requestedUser object (hydrated TV)', () => {
|
||||||
|
const req = {
|
||||||
|
childRequests: [
|
||||||
|
{},
|
||||||
|
{ requestedUser: { userName: 'tv_user', alias: 'tv_alias' } }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
expect(extractRequestedUser(req)).toBe('tv_alias');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('recursively extracts user from childRequests requestedUser as string', () => {
|
||||||
|
const req = {
|
||||||
|
childRequests: [
|
||||||
|
{ requestedUser: 'string_user' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
expect(extractRequestedUser(req)).toBe('string_user');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('extracts user from deeply nested childRequests with requestedByAlias fallback', () => {
|
||||||
|
const req = {
|
||||||
|
childRequests: [
|
||||||
|
{ requestedByAlias: 'deep_alias' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
expect(extractRequestedUser(req)).toBe('deep_alias');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('filterRequestsByUser', () => {
|
describe('filterRequestsByUser', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user