Skip to content

Commit

Permalink
Fixing missing handlers and changing docker behaviour
Browse files Browse the repository at this point in the history
  • Loading branch information
steilerDev committed Jan 4, 2023
1 parent 659f989 commit 06f57b7
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 90 deletions.
10 changes: 5 additions & 5 deletions app/src/app/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ function commanderParseInt(value: string, _dummyPrevious: unknown): number {

function commanderParseCron(value: string, _dummyPrevious: unknown): string {
try {
const job = new Cron(value)
job.stop()
return value
} catch(err) {
throw new InvalidArgumentError(`Not a valid cron pattern. See https://crontab.guru (or for more information on the underlying implementation https://github.com/hexagon/croner#pattern)`)
const job = new Cron(value);
job.stop();
return value;
} catch (err) {
throw new InvalidArgumentError(`Not a valid cron pattern. See https://crontab.guru (or for more information on the underlying implementation https://github.com/hexagon/croner#pattern)`);
}
}

Expand Down
4 changes: 2 additions & 2 deletions app/src/app/icloud-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ export class SyncApp extends iCloudApp {
async run(...eventHandlers: EventHandler[]): Promise<unknown> {
registerObjectsToEventHandlers(eventHandlers, this.photosLibrary, this.syncEngine);
try {
await super.run();
await super.run(...eventHandlers);
return await this.syncEngine.sync();
} catch (err) {
throw new SyncError(`Sync failed`).addCause(err);
Expand Down Expand Up @@ -305,7 +305,7 @@ export class ArchiveApp extends SyncApp {
async run(...eventHandlers: EventHandler[]): Promise<unknown> {
registerObjectsToEventHandlers(eventHandlers, this.archiveEngine);
try {
const [remoteAssets] = await super.run() as [Asset[], Album[]];
const [remoteAssets] = await super.run(...eventHandlers) as [Asset[], Album[]];
await this.archiveEngine.archivePath(this.archivePath, remoteAssets);
return;
} catch (err) {
Expand Down
50 changes: 12 additions & 38 deletions app/test/_helpers/app-factory.helper.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import EventEmitter from "events";
import { EventHandler } from "../../src/app/event/event-handler";
import {iCloudApp} from "../../src/app/icloud-app";
import {iCloud} from "../../src/lib/icloud/icloud";
import {PhotosLibrary} from "../../src/lib/photos-library/photos-library";
import { spyOnEvent } from "./_general";

/**
* Creates an iCloudApp object populated for testing
Expand Down Expand Up @@ -34,7 +31,7 @@ export const rejectOptions = [
{
"options": [],
"_desc": `No options`,
"expected": `error: required option '-u, --username <email>' not specified\n`
"expected": `error: required option '-u, --username <email>' not specified\n`,
}, {
"options": [
`/usr/bin/node`,
Expand All @@ -56,7 +53,7 @@ export const rejectOptions = [
`token`,
],
"_desc": `Missing username & password`,
"expected": `error: required option '-u, --username <email>' not specified\n`
"expected": `error: required option '-u, --username <email>' not specified\n`,
}, {
"options": [
`/usr/bin/node`,
Expand All @@ -80,7 +77,7 @@ export const rejectOptions = [
`token`,
],
"_desc": `Missing password`,
"expected": `error: required option '-p, --password <password>' not specified\n`
"expected": `error: required option '-p, --password <password>' not specified\n`,
}, {
"options": [
`/usr/bin/node`,
Expand All @@ -106,7 +103,7 @@ export const rejectOptions = [
`token`,
],
"_desc": `Invalid port`,
"expected": `error: option '-P, --port <number>' argument 'eight' is invalid. Not a number.\n`
"expected": `error: option '-P, --port <number>' argument 'eight' is invalid. Not a number.\n`,
}, {
"options": [
`/usr/bin/node`,
Expand All @@ -132,7 +129,7 @@ export const rejectOptions = [
`token`,
],
"_desc": `Invalid log level`,
"expected": `error: option '-l, --log-level <level>' argument 'superInfo' is invalid. Allowed choices are trace, debug, info, warn, error.\n`
"expected": `error: option '-l, --log-level <level>' argument 'superInfo' is invalid. Allowed choices are trace, debug, info, warn, error.\n`,
}, {
"options": [
`/usr/bin/node`,
Expand All @@ -158,7 +155,7 @@ export const rejectOptions = [
`token`,
],
"_desc": `Invalid download threads`,
"expected": `error: option '-t, --download-threads <number>' argument 'five' is invalid. Not a number.\n`
"expected": `error: option '-t, --download-threads <number>' argument 'five' is invalid. Not a number.\n`,
}, {
"options": [
`/usr/bin/node`,
Expand All @@ -184,7 +181,7 @@ export const rejectOptions = [
`token`,
],
"_desc": `Invalid retries`,
"expected": `error: option '-r, --max-retries <number>' argument 'inf' is invalid. Not a number.\n`
"expected": `error: option '-r, --max-retries <number>' argument 'inf' is invalid. Not a number.\n`,
}, {
"options": [
`/usr/bin/node`,
Expand All @@ -208,8 +205,8 @@ export const rejectOptions = [
`archive`,
],
"_desc": `Missing archive path`,
"expected": `error: missing required argument 'path'\n`
},{
"expected": `error: missing required argument 'path'\n`,
}, {
"options": [
`/usr/bin/node`,
`/home/icloud-photos-sync/main.js`,
Expand All @@ -234,8 +231,8 @@ export const rejectOptions = [
`daemon`,
],
"_desc": `Missformatted schedule`,
"expected": `error: option '-S, --schedule <cron-string>' argument 'asdf' is invalid. Not a valid cron pattern. See https://crontab.guru (or for more information on the underlying implementation https://github.com/hexagon/croner#pattern)\n`
}
"expected": `error: option '-S, --schedule <cron-string>' argument 'asdf' is invalid. Not a valid cron pattern. See https://crontab.guru (or for more information on the underlying implementation https://github.com/hexagon/croner#pattern)\n`,
},
];

export const validOptions = {
Expand Down Expand Up @@ -381,27 +378,4 @@ export const validOptions = {
`--refresh-token`,
`--remote-delete`,
],
};

export class MockedEventHandler implements EventHandler {
events: string[]
listeners: any = {}

constructor(...events: string[]) {
this.events = events
}

// Automatically creating listener functions for the desired events and puts them into the listener object
registerObjects(...objects: EventEmitter[]): void {
objects.forEach((obj) => {
if(!this.listeners[obj.constructor.name]) {
this.listeners[obj.constructor.name] = {}
}
this.events.forEach((event) => {
this.listeners[obj.constructor.name][event] = spyOnEvent(obj, event)
})
})
throw new Error("Method not implemented.");
}

}
};
13 changes: 4 additions & 9 deletions app/test/_mocks/logger.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import log from 'loglevel';
import {OptionValues} from 'commander';
import {jest} from '@jest/globals';

/**
* Mocked logger setup
* @param cliOpts - Ignored
*/
export const setupLogger: (OptionValues) => void = jest.fn()
export const setupLogger: () => void = jest.fn();

/**
* Mocked log file name
Expand All @@ -17,6 +12,6 @@ export const logFile = `test`;
* @returns The default logger silenced
*/
export const getLogger: (_instance: any) => log.Logger = jest.fn(() => {
log.default.setLevel(process.env?.DEBUG ? "DEBUG" : "silent")
return log.default
})
log.default.setLevel(process.env?.DEBUG ? `DEBUG` : `silent`);
return log.default;
});
66 changes: 32 additions & 34 deletions app/test/unit/app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {ArchiveError, iCloudError, LibraryError, SyncError} from '../../src/app/
import {spyOnEvent} from '../_helpers/_general';
import {EVENTS} from '../../src/lib/icloud/constants';
import path from 'path';
import { setupLogger } from '../_mocks/logger';
import {setupLogger} from '../_mocks/logger';

describe(`Unit Tests - iCloud App`, () => {
beforeEach(() => {
Expand Down Expand Up @@ -48,7 +48,7 @@ describe(`Unit Tests - iCloud App`, () => {
test(`Create Sync App`, () => {
const syncApp = appFactory(validOptions.sync) as SyncApp;
expect(syncApp).toBeInstanceOf(SyncApp);
expect(setupLogger).toHaveBeenCalledTimes(1)
expect(setupLogger).toHaveBeenCalledTimes(1);
expect(syncApp.icloud).toBeDefined();
expect(syncApp.icloud.mfaServer).toBeDefined();
expect(syncApp.icloud.auth).toBeDefined();
Expand All @@ -60,7 +60,7 @@ describe(`Unit Tests - iCloud App`, () => {
test(`Create Archive App`, () => {
const archiveApp = appFactory(validOptions.archive) as ArchiveApp;
expect(archiveApp).toBeInstanceOf(ArchiveApp);
expect(setupLogger).toHaveBeenCalledTimes(1)
expect(setupLogger).toHaveBeenCalledTimes(1);
expect(archiveApp.icloud).toBeDefined();
expect(archiveApp.icloud.mfaServer).toBeDefined();
expect(archiveApp.icloud.auth).toBeDefined();
Expand All @@ -76,8 +76,8 @@ describe(`Unit Tests - iCloud App`, () => {
])(`Create Daemon App - $mode`, ({options}) => {
const daemonApp = appFactory(options) as DaemonApp;
expect(daemonApp).toBeInstanceOf(DaemonApp);
expect(setupLogger).toHaveBeenCalledTimes(1)
expect(daemonApp.event).toBeDefined()
expect(setupLogger).toHaveBeenCalledTimes(1);
expect(daemonApp.event).toBeDefined();
});
});

Expand Down Expand Up @@ -199,51 +199,49 @@ describe(`Unit Tests - iCloud App`, () => {

describe(`Daemon App`, () => {
test(`Schedule job`, async () => {
const daemonApp = appFactory(validOptions.daemon) as DaemonApp
daemonApp.performScheduledSync = jest.fn(() => Promise.resolve())
daemonApp.options.schedule = "*/1 * * * * *" // Every second
const eventsScheduledEvent = spyOnEvent(daemonApp.event, DaemonAppEvents.EVENTS.SCHEDULED)
const daemonApp = appFactory(validOptions.daemon) as DaemonApp;
daemonApp.performScheduledSync = jest.fn(() => Promise.resolve());
daemonApp.options.schedule = `*/1 * * * * *`; // Every second
const eventsScheduledEvent = spyOnEvent(daemonApp.event, DaemonAppEvents.EVENTS.SCHEDULED);

await daemonApp.run()
await daemonApp.run();

expect(eventsScheduledEvent).toHaveBeenCalledTimes(1)
expect(eventsScheduledEvent).toHaveBeenCalledTimes(1);
// Waiting 2 seconds to make sure schedule ran at least once
await new Promise((r) => setTimeout(r, 2000));
expect(daemonApp.performScheduledSync).toHaveBeenCalled()
await new Promise(r => setTimeout(r, 2000));
expect(daemonApp.performScheduledSync).toHaveBeenCalled();

daemonApp.job?.stop()
})
daemonApp.job?.stop();
});

test(`Scheduled sync succeeds`, async () => {
const syncApp = appFactory(validOptions.sync) as SyncApp
syncApp.run = jest.fn(() => Promise.resolve())
const syncApp = appFactory(validOptions.sync) as SyncApp;
syncApp.run = jest.fn(() => Promise.resolve());

const daemonApp = appFactory(validOptions.daemon) as DaemonApp
const successEvent = spyOnEvent(daemonApp.event, DaemonAppEvents.EVENTS.DONE)
const daemonApp = appFactory(validOptions.daemon) as DaemonApp;
const successEvent = spyOnEvent(daemonApp.event, DaemonAppEvents.EVENTS.DONE);

await daemonApp.performScheduledSync([], syncApp)
await daemonApp.performScheduledSync([], syncApp);

expect(syncApp.run).toHaveBeenCalled()
expect(successEvent).toHaveBeenCalled()
})
expect(syncApp.run).toHaveBeenCalled();
expect(successEvent).toHaveBeenCalled();
});

test(`Scheduled sync fails`, async () => {
const syncApp = appFactory(validOptions.sync) as SyncApp
syncApp.run = jest.fn(() => Promise.reject())
const syncApp = appFactory(validOptions.sync) as SyncApp;
syncApp.run = jest.fn(() => Promise.reject());

const daemonApp = appFactory(validOptions.daemon) as DaemonApp
const retryEvent = spyOnEvent(daemonApp.event, DaemonAppEvents.EVENTS.RETRY)
const daemonApp = appFactory(validOptions.daemon) as DaemonApp;
const retryEvent = spyOnEvent(daemonApp.event, DaemonAppEvents.EVENTS.RETRY);

await daemonApp.performScheduledSync([], syncApp)
await daemonApp.performScheduledSync([], syncApp);

expect(syncApp.run).toHaveBeenCalled()
expect(retryEvent).toHaveBeenCalled()
})
})
expect(syncApp.run).toHaveBeenCalled();
expect(retryEvent).toHaveBeenCalled();
});
});
});



describe(`Library Lock`, () => {
test(`Acquire lock`, async () => {
const tokenApp = appFactory(validOptions.token) as TokenApp;
Expand Down
3 changes: 1 addition & 2 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,4 @@ RUN chmod 755 /root/enter_mfa.sh && \
chmod 755 /root/resend_mfa.sh && \
ln -s /root/resend_mfa.sh /usr/local/bin/resend_mfa

ENTRYPOINT ["icloud-photos-sync"]
CMD ["sync"]
ENTRYPOINT ["icloud-photos-sync"]

0 comments on commit 06f57b7

Please sign in to comment.