Skip to content

Commit 0e99f2b

Browse files
committed
feat(adapters): accept job class matchers for assertion
1 parent c57541a commit 0e99f2b

File tree

4 files changed

+73
-7
lines changed

4 files changed

+73
-7
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,8 +233,8 @@ const adapter = QueueManager.fake()
233233

234234
await SendEmailJob.dispatch({ to: '[email protected]' })
235235

236-
adapter.assertPushed('SendEmailJob')
237-
adapter.assertPushed('SendEmailJob', {
236+
adapter.assertPushed(SendEmailJob)
237+
adapter.assertPushed(SendEmailJob, {
238238
queue: 'default',
239239
payload: (payload) => payload.to === '[email protected]',
240240
})

src/drivers/fake_adapter.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { CronExpressionParser } from 'cron-parser'
55
import type { Adapter, AcquiredJob } from '../contracts/adapter.js'
66
import type {
77
JobData,
8+
JobClass,
89
JobRecord,
910
JobRetention,
1011
ScheduleConfig,
@@ -13,6 +14,7 @@ import type {
1314
} from '../types/main.js'
1415
import { DEFAULT_PRIORITY } from '../constants.js'
1516
import { parse } from '../utils.js'
17+
import { Job } from '../job.js'
1618

1719
interface ActiveJob {
1820
job: JobData
@@ -33,7 +35,7 @@ export interface FakeJobRecord {
3335
pushedAt: number
3436
}
3537

36-
export type FakeJobMatcher = string | ((job: JobData) => boolean)
38+
export type FakeJobMatcher = string | JobClass | ((job: JobData) => boolean)
3739
export type FakePayloadMatcher =
3840
| ((payload: any) => boolean)
3941
| object
@@ -539,7 +541,11 @@ export class FakeAdapter implements Adapter {
539541
}
540542

541543
const matchesJob =
542-
typeof matcher === 'string' ? record.job.name === matcher : matcher(record.job)
544+
typeof matcher === 'string'
545+
? record.job.name === matcher
546+
: this.#isJobClass(matcher)
547+
? record.job.name === this.#getJobClassName(matcher)
548+
: matcher(record.job)
543549

544550
if (!matchesJob) {
545551
return false
@@ -575,8 +581,9 @@ export class FakeAdapter implements Adapter {
575581
#formatFailure(prefix: string, matcher: FakeJobMatcher, query?: FakeJobQuery): string {
576582
const parts = [prefix]
577583

578-
if (typeof matcher === 'string') {
579-
parts.push(`for "${matcher}"`)
584+
const matcherName = this.#getMatcherName(matcher)
585+
if (matcherName) {
586+
parts.push(`for "${matcherName}"`)
580587
}
581588

582589
if (query?.queue) {
@@ -597,4 +604,24 @@ export class FakeAdapter implements Adapter {
597604

598605
return `${parts.join(' ')}. ${suffix}.`
599606
}
607+
608+
#getMatcherName(matcher: FakeJobMatcher): string | undefined {
609+
if (typeof matcher === 'string') {
610+
return matcher
611+
}
612+
613+
if (this.#isJobClass(matcher)) {
614+
return this.#getJobClassName(matcher)
615+
}
616+
617+
return undefined
618+
}
619+
620+
#isJobClass(matcher: FakeJobMatcher): matcher is JobClass {
621+
return typeof matcher === 'function' && matcher.prototype instanceof Job
622+
}
623+
624+
#getJobClassName(JobClass: JobClass): string {
625+
return JobClass.options?.name || JobClass.name
626+
}
600627
}

src/queue_manager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ class QueueManagerSingleton {
197197
*
198198
* await SendEmailJob.dispatch({ to: 'user@example.com' })
199199
*
200-
* fake.assertPushed('SendEmailJob')
200+
* fake.assertPushed(SendEmailJob)
201201
* QueueManager.restore()
202202
* ```
203203
*/

tests/fake_adapter.spec.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { setTimeout } from 'node:timers/promises'
22
import { test } from '@japa/runner'
3+
import { Job } from '../src/job.js'
34
import { fake } from '../src/drivers/fake_adapter.js'
45

56
test.group('FakeAdapter', () => {
@@ -87,6 +88,44 @@ test.group('FakeAdapter', () => {
8788
await adapter.destroy()
8889
})
8990

91+
test('should support job class matchers', async ({ assert }) => {
92+
const adapter = fake()()
93+
94+
class SendEmailJob extends Job<{ to: string }> {
95+
async execute() {}
96+
}
97+
98+
class CustomNamedJob extends Job {
99+
static options = { name: 'CustomJob' }
100+
async execute() {}
101+
}
102+
103+
await adapter.pushOn('default', {
104+
id: 'job-1',
105+
name: 'SendEmailJob',
106+
payload: { to: '[email protected]' },
107+
attempts: 0,
108+
})
109+
110+
await adapter.pushOn('default', {
111+
id: 'job-2',
112+
name: 'CustomJob',
113+
payload: null,
114+
attempts: 0,
115+
})
116+
117+
adapter.assertPushed(SendEmailJob)
118+
adapter.assertPushed(CustomNamedJob)
119+
120+
class MissingJob extends Job {
121+
async execute() {}
122+
}
123+
124+
assert.throws(() => adapter.assertPushed(MissingJob))
125+
126+
await adapter.destroy()
127+
})
128+
90129
test('should recover stalled jobs only for targeted queue', async ({ assert }) => {
91130
const adapter = fake()()
92131

0 commit comments

Comments
 (0)