bug: StatsSchema zod validation error (#1267)

## Description:

Fix a bug in the StatsSchema zod validation logic. In zod v4, the
`record` function has been renamed to `partialRecord`, and the new
`record` function requires that all keys are present. We intentionally
omit empty stats, so this causes a zod validation error.

Example error:

```
Connection error!
 game id: VSEtmKpJ, client id: 0UMrA84F
Error: ✖ Invalid input: expected array, received undefined
  → at allPlayersStats.0UMrA84F.units.wshp
✖ Invalid input: expected array, received undefined
  → at allPlayersStats.0UMrA84F.units.saml
Stack: 
```

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors
This commit is contained in:
Scott Anderson
2025-06-24 16:50:36 -04:00
committed by GitHub
parent 4680b04656
commit 13f0c2303e
2 changed files with 51 additions and 3 deletions
+3 -3
View File
@@ -98,10 +98,10 @@ export const PlayerStatsSchema = z
.object({
attacks: AtLeastOneNumberSchema.optional(),
betrayals: BigIntStringSchema.optional(),
boats: z.record(BoatUnitSchema, AtLeastOneNumberSchema).optional(),
bombs: z.record(BombUnitSchema, AtLeastOneNumberSchema).optional(),
boats: z.partialRecord(BoatUnitSchema, AtLeastOneNumberSchema).optional(),
bombs: z.partialRecord(BombUnitSchema, AtLeastOneNumberSchema).optional(),
gold: AtLeastOneNumberSchema.optional(),
units: z.record(OtherUnitSchema, AtLeastOneNumberSchema).optional(),
units: z.partialRecord(OtherUnitSchema, AtLeastOneNumberSchema).optional(),
})
.optional();
export type PlayerStats = z.infer<typeof PlayerStatsSchema>;
+48
View File
@@ -0,0 +1,48 @@
import { PlayerStatsSchema } from "../src/core/StatsSchemas";
function testPlayerSchema(
json: string,
expectSuccess = true,
expectThrow = false,
): void {
const parse = () => {
const raw = JSON.parse(json);
const result = PlayerStatsSchema.safeParse(raw);
return result.success;
};
if (expectSuccess) {
// Expect success
expect(parse()).toBeTruthy();
} else if (!expectThrow) {
// Expect failure
expect(parse()).toBeFalsy();
} else {
// Expect throw
expect(parse).toThrow();
}
}
describe("StatsSchema", () => {
test("Parse empty", () => {
testPlayerSchema("{}");
});
test("Parse partial", () => {
testPlayerSchema('{"units":{"port":["0","0","0","1"]}}');
});
test("Parse invalid", () => {
testPlayerSchema("[]", false);
testPlayerSchema("null", false);
testPlayerSchema('"null"', false);
testPlayerSchema('"undefined"', false);
});
test("Parse failure", () => {
testPlayerSchema("", false, true);
testPlayerSchema("undefined", false, true);
testPlayerSchema("{", false, true);
testPlayerSchema("{}}", false, true);
});
});