Skip to content

Commit aa99a9c

Browse files
troxewvdfdevvdfdev
authored
feat: Conditional log redacting in long form move (#1089)
* test: Add conditional redact cases * feat: Conditional log redacting in long form move * Reduces nesting Co-authored-by: Vinny <1034631+vdfdev@users.noreply.github.com> Co-authored-by: vdfdev <me@vdf.dev>
1 parent 4bf203c commit aa99a9c

4 files changed

Lines changed: 130 additions & 1 deletion

File tree

src/core/reducer.test.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,62 @@ describe('Random inside setup()', () => {
415415
});
416416
});
417417

418+
describe('redact', () => {
419+
const game: Game = {
420+
setup: () => ({
421+
isASecret: false,
422+
}),
423+
moves: {
424+
A: {
425+
move: (G) => G,
426+
redact: (G) => G.isASecret,
427+
},
428+
B: (G) => {
429+
return { ...G, isASecret: true };
430+
},
431+
},
432+
};
433+
434+
const reducer = CreateGameReducer({ game });
435+
436+
let state = InitializeGame({ game });
437+
438+
test('move A is not secret and is not redact', () => {
439+
state = reducer(state, makeMove('A', ['not redact'], '0'));
440+
expect(state.G).toMatchObject({
441+
isASecret: false,
442+
});
443+
const [lastLogEntry] = state.deltalog.slice(-1);
444+
expect(lastLogEntry).toMatchObject({
445+
action: {
446+
payload: {
447+
type: 'A',
448+
args: ['not redact'],
449+
},
450+
},
451+
redact: false,
452+
});
453+
});
454+
455+
test('move A is secret and is redact', () => {
456+
state = reducer(state, makeMove('B', ['not redact'], '0'));
457+
state = reducer(state, makeMove('A', ['redact'], '0'));
458+
expect(state.G).toMatchObject({
459+
isASecret: true,
460+
});
461+
const [lastLogEntry] = state.deltalog.slice(-1);
462+
expect(lastLogEntry).toMatchObject({
463+
action: {
464+
payload: {
465+
type: 'A',
466+
args: ['redact'],
467+
},
468+
},
469+
redact: true,
470+
});
471+
});
472+
});
473+
418474
describe('undo / redo', () => {
419475
const game: Game = {
420476
seed: 0,

src/core/reducer.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ function initializeDeltalog(
120120

121121
if (typeof move === 'object' && move.redact === true) {
122122
logEntry.redact = true;
123+
} else if (typeof move === 'object' && move.redact instanceof Function) {
124+
logEntry.redact = move.redact(state.G, state.ctx);
123125
}
124126

125127
return {

src/master/filter-player-view.test.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,3 +308,74 @@ describe('redactLog', () => {
308308
]);
309309
});
310310
});
311+
312+
test('make move args to be secret depends on G via conditional redact', async () => {
313+
const game = {
314+
setup: () => ({
315+
isASecret: false,
316+
}),
317+
moves: {
318+
A: {
319+
move: (G) => G,
320+
redact: (G) => G.isASecret,
321+
},
322+
B: (G) => {
323+
return { ...G, isASecret: true };
324+
},
325+
},
326+
};
327+
328+
const send = jest.fn();
329+
const master = new Master(game, new InMemory(), TransportAPI(send));
330+
const filterPlayerView = getFilterPlayerView(game);
331+
332+
const actionA0 = ActionCreators.makeMove('A', ['not redacted'], '0');
333+
const actionB = ActionCreators.makeMove('B', ['not redacted'], '0');
334+
const actionA1 = ActionCreators.makeMove('A', ['redacted'], '0');
335+
336+
// test: ping-pong two moves, then sync and check the log
337+
await master.onSync('matchID', '0', undefined, 2);
338+
await master.onUpdate(actionA0, 0, 'matchID', '0');
339+
await master.onUpdate(actionB, 1, 'matchID', '0');
340+
await master.onUpdate(actionA1, 2, 'matchID', '0');
341+
await master.onSync('matchID', '1', undefined, 2);
342+
343+
const payload = send.mock.calls[send.mock.calls.length - 1][0];
344+
expect(
345+
(filterPlayerView('1', payload).args[1] as SyncInfo).log
346+
).toMatchObject([
347+
{
348+
action: {
349+
type: 'MAKE_MOVE',
350+
payload: {
351+
type: 'A',
352+
args: ['not redacted'],
353+
playerID: '0',
354+
},
355+
},
356+
_stateID: 0,
357+
},
358+
{
359+
action: {
360+
type: 'MAKE_MOVE',
361+
payload: {
362+
type: 'B',
363+
args: ['not redacted'],
364+
playerID: '0',
365+
},
366+
},
367+
_stateID: 1,
368+
},
369+
{
370+
action: {
371+
type: 'MAKE_MOVE',
372+
payload: {
373+
type: 'A',
374+
args: null,
375+
playerID: '0',
376+
},
377+
},
378+
_stateID: 2,
379+
},
380+
]);
381+
});

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ export interface LongFormMove<
203203
CtxWithPlugins extends Ctx = Ctx
204204
> {
205205
move: MoveFn<G, CtxWithPlugins>;
206-
redact?: boolean;
206+
redact?: boolean | ((G: G, ctx: CtxWithPlugins) => boolean);
207207
noLimit?: boolean;
208208
client?: boolean;
209209
undoable?: boolean | ((G: G, ctx: CtxWithPlugins) => boolean);

0 commit comments

Comments
 (0)