merge: complete map-to-city page parity
This commit is contained in:
@@ -53,13 +53,13 @@ const general = (overrides: Partial<GeneralRow> = {}): GeneralRow => ({
|
||||
updatedAt: now,
|
||||
...overrides,
|
||||
});
|
||||
const auth = (roles: string[] = []): GameSessionTokenPayload => ({
|
||||
const auth = (roles: string[] = [], userId = 'user-1'): GameSessionTokenPayload => ({
|
||||
version: 1,
|
||||
profile: 'che:default',
|
||||
issuedAt: now.toISOString(),
|
||||
expiresAt: new Date(now.getTime() + 86400000).toISOString(),
|
||||
sessionId: 'session',
|
||||
user: { id: 'user-1', username: 'tester', displayName: 'Tester', roles },
|
||||
user: { id: userId, username: 'tester', displayName: 'Tester', roles },
|
||||
sanctions: {},
|
||||
});
|
||||
const city = (id: number, nationId: number) => ({
|
||||
@@ -88,15 +88,30 @@ const city = (id: number, nationId: number) => ({
|
||||
meta: {},
|
||||
});
|
||||
|
||||
const context = (options: { me?: GeneralRow; roles?: string[]; nationMeta?: Record<string, unknown> } = {}) => {
|
||||
const context = (
|
||||
options: {
|
||||
me?: GeneralRow;
|
||||
roles?: string[];
|
||||
userId?: string;
|
||||
nationMeta?: Record<string, unknown>;
|
||||
nationLevel?: number;
|
||||
stationCityId?: number;
|
||||
} = {}
|
||||
) => {
|
||||
const me = options.me ?? general();
|
||||
const cities = [city(1, 1), city(2, 2), city(3, 2), city(80, 1)];
|
||||
const foreign = general({ id: 2, userId: 'user-2', name: '적군', nationId: 2, cityId: 2, crew: 777 });
|
||||
const db = {
|
||||
general: {
|
||||
findFirst: vi.fn(async () => me),
|
||||
findFirst: vi.fn(async ({ where }: { where: { userId: string } }) =>
|
||||
where.userId === me.userId ? me : null
|
||||
),
|
||||
findMany: vi.fn(async (args: { where?: Record<string, unknown>; select?: Record<string, boolean> }) => {
|
||||
if (args.where?.nationId === 1 && args.select?.cityId) return [{ cityId: me.cityId }];
|
||||
if (args.where?.nationId === 1 && args.select?.cityId)
|
||||
return [
|
||||
{ cityId: me.cityId },
|
||||
...(options.stationCityId ? [{ cityId: options.stationCityId }] : []),
|
||||
];
|
||||
if (args.where?.cityId === 2) return [foreign];
|
||||
if (args.where?.cityId === 3) return [foreign];
|
||||
if (args.where?.officerLevel) return [];
|
||||
@@ -108,7 +123,7 @@ const context = (options: { me?: GeneralRow; roles?: string[]; nationMeta?: Reco
|
||||
id: 1,
|
||||
name: '아국',
|
||||
color: '#008000',
|
||||
level: 1,
|
||||
level: options.nationLevel ?? 1,
|
||||
capitalCityId: 1,
|
||||
meta: options.nationMeta ?? {},
|
||||
})),
|
||||
@@ -130,7 +145,7 @@ const context = (options: { me?: GeneralRow; roles?: string[]; nationMeta?: Reco
|
||||
turnDaemon: {} as GameApiContext['turnDaemon'],
|
||||
battleSim: {} as GameApiContext['battleSim'],
|
||||
profile: { id: 'che', scenario: 'default', name: 'che:default' },
|
||||
auth: auth(options.roles),
|
||||
auth: auth(options.roles, options.userId),
|
||||
uploadDir: 'uploads',
|
||||
uploadPath: '/uploads',
|
||||
uploadPublicUrl: null,
|
||||
@@ -157,6 +172,25 @@ describe('in-game information permissions', () => {
|
||||
expect(result.generals).toEqual([]);
|
||||
});
|
||||
|
||||
it('derives the actor from the session user instead of accepting another user general', async () => {
|
||||
const caller = appRouter.createCaller(context({ userId: 'user-2' }));
|
||||
await expect(caller.world.getCurrentCity({ cityId: 1 })).rejects.toMatchObject({ code: 'NOT_FOUND' });
|
||||
});
|
||||
|
||||
it('allows a nation member to select a city occupied by another general of the same nation', async () => {
|
||||
const result = await appRouter.createCaller(context({ stationCityId: 2 })).world.getCurrentCity({ cityId: 2 });
|
||||
expect(result.options.map((entry) => entry.id)).toContain(2);
|
||||
expect(result.visibility.full).toBe(true);
|
||||
});
|
||||
|
||||
it('does not grant a spy city while the nation has no active level', async () => {
|
||||
const result = await appRouter
|
||||
.createCaller(context({ nationMeta: { spy: { 2: 2 } }, nationLevel: 0 }))
|
||||
.world.getCurrentCity({ cityId: 2 });
|
||||
expect(result.options.map((entry) => entry.id)).not.toContain(2);
|
||||
expect(result.visibility.full).toBe(false);
|
||||
});
|
||||
|
||||
it('keeps adjacent foreign detail redacted and never reveals military fields', async () => {
|
||||
const result = await appRouter
|
||||
.createCaller(context({ me: general({ cityId: 80 }) }))
|
||||
@@ -174,12 +208,20 @@ describe('in-game information permissions', () => {
|
||||
expect(result.visibility.full).toBe(true);
|
||||
expect(result.city.population).toBe(1000);
|
||||
expect(result.generals[0]).toMatchObject({ crew: 777, train: null, atmos: null, crewTypeId: null });
|
||||
expect(result.forceSummary).toMatchObject({
|
||||
enemyCrew: 777,
|
||||
enemyArmedGenerals: 1,
|
||||
enemyGenerals: 1,
|
||||
});
|
||||
});
|
||||
|
||||
it('allows administrative roles to inspect all city and general fields', async () => {
|
||||
const result = await appRouter.createCaller(context({ roles: ['admin'] })).world.getCurrentCity({ cityId: 3 });
|
||||
expect(result.options).toHaveLength(4);
|
||||
expect(result.visibility.full).toBe(true);
|
||||
expect(result.generals[0]).toMatchObject({ crew: 777, train: 90, atmos: 90, crewTypeId: 1 });
|
||||
});
|
||||
it.each(['admin', 'superuser', 'admin.superuser'])(
|
||||
'allows the %s role to inspect all city and general fields',
|
||||
async (role) => {
|
||||
const result = await appRouter.createCaller(context({ roles: [role] })).world.getCurrentCity({ cityId: 3 });
|
||||
expect(result.options).toHaveLength(4);
|
||||
expect(result.visibility.full).toBe(true);
|
||||
expect(result.generals[0]).toMatchObject({ crew: 777, train: 90, atmos: 90, crewTypeId: 1 });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user