-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(modules/items/albums): create
AlbumSubscriber
and validate alb…
…um properties after load
- Loading branch information
Showing
3 changed files
with
202 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
import { Test, TestingModule } from '@nestjs/testing' | ||
import { DataSource, EntityManager } from 'typeorm' | ||
import { MockInstance } from 'vitest' | ||
|
||
import { ArtistsService } from '../artists' | ||
|
||
import { Album } from './album.entity' | ||
import { AlbumSubscriber } from './album.subscriber' | ||
|
||
import { | ||
albumEntityMock, | ||
entityManagerFactoryMock, | ||
sdkAlbumMock, | ||
sdkArtistsMock, | ||
transactionFactoryMock, | ||
} from '@common/mocks' | ||
import { SpotifyService } from '@modules/spotify' | ||
|
||
describe('AlbumSubscriber', () => { | ||
let moduleRef: TestingModule | ||
let albumSubscriber: AlbumSubscriber | ||
let spotifyService: SpotifyService | ||
let artistsService: ArtistsService | ||
let entityManagerMock: EntityManager | ||
|
||
beforeEach(async () => { | ||
entityManagerMock = entityManagerFactoryMock() | ||
|
||
moduleRef = await Test.createTestingModule({ | ||
providers: [ | ||
AlbumSubscriber, | ||
{ | ||
provide: DataSource, | ||
useValue: { | ||
subscribers: [], | ||
transaction: transactionFactoryMock(entityManagerMock), | ||
}, | ||
}, | ||
{ | ||
provide: SpotifyService, | ||
useValue: { | ||
albums: { | ||
get: vi.fn(), | ||
}, | ||
artists: { | ||
get: vi.fn(), | ||
}, | ||
}, | ||
}, | ||
{ | ||
provide: ArtistsService, | ||
useValue: { | ||
findOrCreate: vi.fn(), | ||
}, | ||
}, | ||
], | ||
}).compile() | ||
|
||
albumSubscriber = moduleRef.get(AlbumSubscriber) | ||
spotifyService = moduleRef.get(SpotifyService) | ||
artistsService = moduleRef.get(ArtistsService) | ||
}) | ||
|
||
afterEach(() => { | ||
moduleRef.close() | ||
}) | ||
|
||
test('should be defined', () => { | ||
expect(albumSubscriber).toBeDefined() | ||
}) | ||
|
||
test('should listen to album entity', () => { | ||
expect(albumSubscriber.listenTo()).toEqual(Album) | ||
}) | ||
|
||
describe('afterLoad', () => { | ||
let albumMock: Album | ||
|
||
let spotifyGetAlbumSpy: MockInstance | ||
let spotifyGetArtistsSpy: MockInstance | ||
let findOrCreateSpy: MockInstance | ||
let saveSpy: MockInstance | ||
|
||
beforeEach(() => { | ||
albumMock = albumEntityMock | ||
|
||
spotifyGetAlbumSpy = vi.spyOn(spotifyService.albums, 'get') | ||
spotifyGetArtistsSpy = vi.spyOn(spotifyService.artists, 'get') | ||
findOrCreateSpy = vi.spyOn(artistsService, 'findOrCreate') | ||
saveSpy = vi.spyOn(entityManagerMock, 'save') | ||
}) | ||
|
||
test('should skip if album has artists', async () => { | ||
await albumSubscriber.afterLoad(albumMock) | ||
|
||
expect(spotifyGetAlbumSpy).not.toHaveBeenCalled() | ||
expect(spotifyGetArtistsSpy).not.toHaveBeenCalled() | ||
expect(findOrCreateSpy).not.toHaveBeenCalled() | ||
expect(saveSpy).not.toHaveBeenCalled() | ||
}) | ||
|
||
test('should update album with artists if artists are undefined', async () => { | ||
spotifyGetAlbumSpy.mockResolvedValue(sdkAlbumMock) | ||
spotifyGetArtistsSpy.mockResolvedValue(sdkArtistsMock) | ||
|
||
// @ts-expect-error - mocking property | ||
albumMock.artists = undefined | ||
|
||
await albumSubscriber.afterLoad(albumMock) | ||
|
||
expect(spotifyGetAlbumSpy).toHaveBeenCalledWith( | ||
albumMock.externalId, | ||
false | ||
) | ||
expect(spotifyGetArtistsSpy).toHaveBeenCalledWith( | ||
sdkAlbumMock.artists.map(({ id }) => id), | ||
false | ||
) | ||
expect(findOrCreateSpy).toHaveBeenCalledWith( | ||
sdkArtistsMock, | ||
entityManagerMock | ||
) | ||
expect(saveSpy).toHaveBeenCalledWith(albumMock) | ||
}) | ||
|
||
test('should update album with artists if artists are empty', async () => { | ||
spotifyGetAlbumSpy.mockResolvedValue(sdkAlbumMock) | ||
spotifyGetArtistsSpy.mockResolvedValue(sdkArtistsMock) | ||
|
||
albumMock.artists = [] | ||
|
||
await albumSubscriber.afterLoad(albumMock) | ||
|
||
expect(spotifyGetAlbumSpy).toHaveBeenCalledWith( | ||
albumMock.externalId, | ||
false | ||
) | ||
expect(spotifyGetArtistsSpy).toHaveBeenCalledWith( | ||
sdkAlbumMock.artists.map(({ id }) => id), | ||
false | ||
) | ||
expect(findOrCreateSpy).toHaveBeenCalledWith( | ||
sdkArtistsMock, | ||
entityManagerMock | ||
) | ||
expect(saveSpy).toHaveBeenCalledWith(albumMock) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { DataSource, EntitySubscriberInterface, EventSubscriber } from 'typeorm' | ||
import { Logger } from '@nestjs/common' | ||
|
||
import { Album } from './album.entity' | ||
|
||
import { ArtistsService } from '@modules/items/artists' | ||
import { SpotifyService } from '@modules/spotify' | ||
|
||
@EventSubscriber() | ||
export class AlbumSubscriber implements EntitySubscriberInterface<Album> { | ||
private readonly logger = new Logger(AlbumSubscriber.name) | ||
|
||
constructor( | ||
private readonly dataSource: DataSource, | ||
private readonly spotifyService: SpotifyService, | ||
private readonly artistsService: ArtistsService | ||
) { | ||
dataSource.subscribers.push(this) | ||
} | ||
|
||
listenTo() { | ||
return Album | ||
} | ||
|
||
async afterLoad(albumEntity: Album) { | ||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
if (!albumEntity.artists || albumEntity.artists.length === 0) { | ||
this.logger.log(`Inserting artists for album ${albumEntity.name}`) | ||
|
||
const sdkAlbum = await this.spotifyService.albums.get( | ||
albumEntity.externalId, | ||
false | ||
) | ||
const sdkArtists = await this.spotifyService.artists.get( | ||
sdkAlbum.artists.map(({ id }) => id), | ||
false | ||
) | ||
|
||
this.dataSource.transaction(async manager => { | ||
const artists = await this.artistsService.findOrCreate( | ||
sdkArtists, | ||
manager | ||
) | ||
|
||
albumEntity.artists = artists | ||
|
||
await manager.save(albumEntity) | ||
}) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters