Skip to content

Commit b91a3de

Browse files
committed
test(transfer): Add validation tests for transfer feature
1 parent 49d5d35 commit b91a3de

File tree

1 file changed

+199
-0
lines changed

1 file changed

+199
-0
lines changed
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import { loggerTest } from "@foxxmd/logging";
2+
import chai, { assert, expect } from 'chai';
3+
import asPromised from 'chai-as-promised';
4+
import clone from 'clone';
5+
import dayjs from "dayjs";
6+
import EventEmitter from "events";
7+
import { describe, it, beforeEach } from 'mocha';
8+
import { PlayObject } from "../../../core/Atomic.js";
9+
import { TransferManager } from "../../transfer/TransferManager.js";
10+
import { TransferJob } from "../../transfer/TransferJob.js";
11+
import { generatePlay, generatePlays, normalizePlays } from "../utils/PlayTestUtils.js";
12+
import ScrobbleSources from "../../sources/ScrobbleSources.js";
13+
import ScrobbleClients from "../../scrobblers/ScrobbleClients.js";
14+
import AbstractScrobbleClient from "../../scrobblers/AbstractScrobbleClient.js";
15+
import AbstractSource from "../../sources/AbstractSource.js";
16+
import { Notifiers } from "../../notifier/Notifiers.js";
17+
18+
chai.use(asPromised);
19+
20+
const emitter = new EventEmitter();
21+
22+
// Test source that supports getRecentlyPlayed
23+
class TestTransferSource extends AbstractSource {
24+
testPlays: PlayObject[] = [];
25+
26+
constructor(name: string, plays: PlayObject[] = []) {
27+
super('test', name, {}, { localUrl: new URL('https://example.com'), configDir: 'fake', logger: loggerTest, version: 'test' }, emitter);
28+
this.testPlays = plays;
29+
this.canBacklog = false;
30+
// Mark as ready
31+
this.buildOK = true;
32+
this.connectionOK = true;
33+
this.requiresAuth = false;
34+
}
35+
36+
protected async doCheckConnection(): Promise<true | string | undefined> {
37+
return true;
38+
}
39+
40+
async getRecentlyPlayed(options: { limit?: number } = {}): Promise<PlayObject[]> {
41+
const { limit = 200 } = options;
42+
return this.testPlays.slice(0, limit);
43+
}
44+
}
45+
46+
// Test client that supports time-range fetching
47+
class TestTransferClient extends AbstractScrobbleClient {
48+
testScrobbles: PlayObject[] = [];
49+
scrobbledPlays: PlayObject[] = [];
50+
51+
constructor(name: string, supportsTimeRange: boolean = true) {
52+
super('test', name, {}, new Notifiers(emitter, new EventEmitter(), new EventEmitter(), loggerTest), emitter, loggerTest);
53+
this.requiresAuth = false;
54+
this.requiresAuthInteraction = false;
55+
// Mark as ready
56+
this.buildOK = true;
57+
this.connectionOK = true;
58+
59+
if (supportsTimeRange) {
60+
// Add the method to support time-range fetching
61+
(this as any).getScrobblesForTimeRange = async (fromDate: any, toDate: any, limit: number) => {
62+
return this.testScrobbles.filter(s => {
63+
const playDate = s.data.playDate;
64+
if (!playDate) return false;
65+
return playDate.isAfter(fromDate) && playDate.isBefore(toDate);
66+
});
67+
};
68+
}
69+
}
70+
71+
protected async doCheckConnection(): Promise<true | string | undefined> {
72+
return true;
73+
}
74+
75+
protected async doBuildInitData(): Promise<true | string | undefined> {
76+
return true;
77+
}
78+
79+
async doScrobble(playObj: PlayObject): Promise<PlayObject> {
80+
this.scrobbledPlays.push(playObj);
81+
return playObj;
82+
}
83+
84+
async getRecentlyPlayed(limit: number): Promise<PlayObject[]> {
85+
return this.testScrobbles.slice(0, limit);
86+
}
87+
88+
protected async getScrobblesForRefresh(limit: number): Promise<PlayObject[]> {
89+
// Return testScrobbles for duplicate detection
90+
return this.testScrobbles;
91+
}
92+
93+
protected async doParseCache() {
94+
await this.cache.init();
95+
return super.doParseCache();
96+
}
97+
}
98+
99+
describe('TransferManager', function() {
100+
101+
describe('Validation', function() {
102+
103+
let transferManager: TransferManager;
104+
let sources: ScrobbleSources;
105+
let clients: ScrobbleClients;
106+
107+
beforeEach(async function() {
108+
sources = new ScrobbleSources(emitter, {
109+
localUrl: new URL('https://example.com'),
110+
configDir: 'fake',
111+
version: 'test'
112+
}, loggerTest);
113+
clients = new ScrobbleClients(emitter, new EventEmitter(), new URL('https://example.com'), 'fake', loggerTest);
114+
transferManager = new TransferManager(sources, clients, loggerTest);
115+
116+
const source = new TestTransferSource('testSource');
117+
sources.sources.push(source);
118+
119+
const client = new TestTransferClient('testClient', true);
120+
await client.initialize();
121+
clients.clients.push(client);
122+
});
123+
124+
it('Rejects future fromDate', async function() {
125+
await assert.isRejected(
126+
transferManager.startTransfer({
127+
sourceName: 'testSource',
128+
clientName: 'testClient',
129+
fromDate: dayjs().add(1, 'day').format('YYYY-MM-DD')
130+
}),
131+
/fromDate cannot be in the future/
132+
);
133+
});
134+
135+
it('Rejects future toDate', async function() {
136+
await assert.isRejected(
137+
transferManager.startTransfer({
138+
sourceName: 'testSource',
139+
clientName: 'testClient',
140+
toDate: dayjs().add(1, 'day').format('YYYY-MM-DD')
141+
}),
142+
/toDate cannot be in the future/
143+
);
144+
});
145+
146+
it('Rejects backwards date range (fromDate > toDate)', async function() {
147+
await assert.isRejected(
148+
transferManager.startTransfer({
149+
sourceName: 'testSource',
150+
clientName: 'testClient',
151+
fromDate: '2025-01-15',
152+
toDate: '2025-01-10'
153+
}),
154+
/fromDate must be before toDate/
155+
);
156+
});
157+
158+
it('Rejects client without time-range support', async function() {
159+
const unsupportedClient = new TestTransferClient('unsupportedClient', false);
160+
await unsupportedClient.initialize();
161+
clients.clients.push(unsupportedClient);
162+
163+
await assert.isRejected(
164+
transferManager.startTransfer({
165+
sourceName: 'testSource',
166+
clientName: 'unsupportedClient',
167+
playCount: 10
168+
}),
169+
/does not support time-range fetching/
170+
);
171+
});
172+
173+
it('Accepts valid transfer options with playCount', async function() {
174+
const transferId = await transferManager.startTransfer({
175+
sourceName: 'testSource',
176+
clientName: 'testClient',
177+
playCount: 10
178+
});
179+
180+
assert.isString(transferId);
181+
assert.isNotEmpty(transferId);
182+
});
183+
184+
it('Accepts valid transfer options with date range', async function() {
185+
const transferId = await transferManager.startTransfer({
186+
sourceName: 'testSource',
187+
clientName: 'testClient',
188+
fromDate: '2025-01-01',
189+
toDate: '2025-01-10'
190+
});
191+
192+
assert.isString(transferId);
193+
assert.isNotEmpty(transferId);
194+
});
195+
});
196+
197+
});
198+
199+

0 commit comments

Comments
 (0)