fix: FanoutTimelineServiceにリストが空の場合のみダミーIDを挿入するinjectDummyIfEmptyメソッドを追加しました。
This commit is contained in:
parent
33bd93ca40
commit
3fe1d27927
|
|
@ -231,7 +231,7 @@ export class FanoutTimelineEndpointService {
|
||||||
Promise.all(ps.redisTimelines.map((tl, i) => {
|
Promise.all(ps.redisTimelines.map((tl, i) => {
|
||||||
// 有効なソースかつ結果が空だった場合のみダミーを入れる
|
// 有効なソースかつ結果が空だった場合のみダミーを入れる
|
||||||
if (redisResult[i] && redisResult[i].length === 0) {
|
if (redisResult[i] && redisResult[i].length === 0) {
|
||||||
return this.fanoutTimelineService.injectDummy(tl, dummyId);
|
return this.fanoutTimelineService.injectDummyIfEmpty(tl, dummyId);
|
||||||
}
|
}
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -109,10 +109,20 @@ export class FanoutTimelineService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public injectDummy(tl: FanoutTimelineName, id: string) {
|
injectDummy(tl: FanoutTimelineName, id: string) {
|
||||||
return this.redisForTimelines.lpush('list:' + tl, id);
|
return this.redisForTimelines.lpush('list:' + tl, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public injectDummyIfEmpty(tl: FanoutTimelineName, id: string): Promise<boolean> {
|
||||||
|
return this.redisForTimelines.eval(
|
||||||
|
'if redis.call("LLEN", KEYS[1]) == 0 then redis.call("LPUSH", KEYS[1], ARGV[1]) return 1 else return 0 end',
|
||||||
|
1,
|
||||||
|
'list:' + tl,
|
||||||
|
id,
|
||||||
|
).then(res => res === 1);
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public purge(name: FanoutTimelineName) {
|
public purge(name: FanoutTimelineName) {
|
||||||
return this.redisForTimelines.del('list:' + name);
|
return this.redisForTimelines.del('list:' + name);
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ describe('FanoutTimelineEndpointService', () => {
|
||||||
.useValue({
|
.useValue({
|
||||||
getMulti: jest.fn(),
|
getMulti: jest.fn(),
|
||||||
injectDummy: jest.fn(),
|
injectDummy: jest.fn(),
|
||||||
|
injectDummyIfEmpty: jest.fn(),
|
||||||
})
|
})
|
||||||
.compile();
|
.compile();
|
||||||
|
|
||||||
|
|
@ -266,9 +267,9 @@ describe('FanoutTimelineEndpointService', () => {
|
||||||
|
|
||||||
expect(result).toEqual([]);
|
expect(result).toEqual([]);
|
||||||
// Should have tried to inject dummy ID for both empty timelines
|
// Should have tried to inject dummy ID for both empty timelines
|
||||||
expect(fanoutTimelineService.injectDummy).toHaveBeenCalledTimes(2);
|
expect(fanoutTimelineService.injectDummyIfEmpty).toHaveBeenCalledTimes(2);
|
||||||
expect(fanoutTimelineService.injectDummy).toHaveBeenCalledWith(`homeTimeline:${alice.id}`, expect.any(String));
|
expect(fanoutTimelineService.injectDummyIfEmpty).toHaveBeenCalledWith(`homeTimeline:${alice.id}`, expect.any(String));
|
||||||
expect(fanoutTimelineService.injectDummy).toHaveBeenCalledWith('localTimeline', expect.any(String));
|
expect(fanoutTimelineService.injectDummyIfEmpty).toHaveBeenCalledWith('localTimeline', expect.any(String));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test for behavior when dummy ID exists
|
// Test for behavior when dummy ID exists
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describe, jest, test, expect, afterEach, beforeAll, afterAll } from '@jest/globals';
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import * as Redis from 'ioredis';
|
||||||
|
import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
|
||||||
|
import { IdService } from '@/core/IdService.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
|
||||||
|
describe('FanoutTimelineService', () => {
|
||||||
|
let app: TestingModule;
|
||||||
|
let service: FanoutTimelineService;
|
||||||
|
let redisForTimelines: jest.Mocked<Redis.Redis>;
|
||||||
|
let idService: IdService;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
app = await Test.createTestingModule({
|
||||||
|
providers: [
|
||||||
|
FanoutTimelineService,
|
||||||
|
{
|
||||||
|
provide: IdService,
|
||||||
|
useValue: {
|
||||||
|
parse: jest.fn(),
|
||||||
|
gen: jest.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: DI.redisForTimelines,
|
||||||
|
useValue: {
|
||||||
|
eval: jest.fn(),
|
||||||
|
lpush: jest.fn(),
|
||||||
|
lrange: jest.fn(),
|
||||||
|
del: jest.fn(),
|
||||||
|
pipeline: jest.fn(() => ({
|
||||||
|
lpush: jest.fn(),
|
||||||
|
ltrim: jest.fn(),
|
||||||
|
lrange: jest.fn(),
|
||||||
|
exec: jest.fn(),
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
app.enableShutdownHooks();
|
||||||
|
|
||||||
|
service = app.get<FanoutTimelineService>(FanoutTimelineService);
|
||||||
|
redisForTimelines = app.get(DI.redisForTimelines);
|
||||||
|
idService = app.get<IdService>(IdService);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await app.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('injectDummyIfEmpty should call Redis EVAL with correct script', async () => {
|
||||||
|
redisForTimelines.eval.mockResolvedValue(1);
|
||||||
|
|
||||||
|
const result = await service.injectDummyIfEmpty('homeTimeline:123', 'dummyId');
|
||||||
|
|
||||||
|
expect(redisForTimelines.eval).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('if redis.call("LLEN", KEYS[1]) == 0 then'),
|
||||||
|
1,
|
||||||
|
'list:homeTimeline:123',
|
||||||
|
'dummyId',
|
||||||
|
);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('injectDummyIfEmpty should return false if list is not empty', async () => {
|
||||||
|
redisForTimelines.eval.mockResolvedValue(0);
|
||||||
|
|
||||||
|
const result = await service.injectDummyIfEmpty('homeTimeline:123', 'dummyId');
|
||||||
|
|
||||||
|
expect(redisForTimelines.eval).toHaveBeenCalled();
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue