@@ -2,6 +2,7 @@ import { Metadata, type ServiceError, status } from "@grpc/grpc-js";
22import { afterEach , beforeEach , describe , expect , it , type MockInstance , vi } from "vitest" ;
33import { ConfigClient } from "../src/client.js" ;
44import {
5+ ChecksumMismatchError ,
56 DecreeError ,
67 IncompatibleServerError ,
78 NotFoundError ,
@@ -284,10 +285,103 @@ describe("ConfigClient", () => {
284285 tenantId : "tenant-1" ,
285286 fieldPath : "payments.fee" ,
286287 value : undefined ,
288+ expectedChecksum : undefined ,
287289 } ) ;
288290 } ) ;
289291 } ) ;
290292
293+ describe ( "expectedChecksum plumbing" , ( ) => {
294+ it ( "set() passes expectedChecksum to proto" , async ( ) => {
295+ configStub . setField . mockImplementation (
296+ ( _req : unknown , _meta : unknown , _opts : unknown , cb : ( ...args : unknown [ ] ) => void ) => {
297+ cb ( null , { configVersion : { version : 1 } } ) ;
298+ } ,
299+ ) ;
300+
301+ await client . set ( "tenant-1" , "payments.fee" , "0.5%" , { expectedChecksum : "abc123" } ) ;
302+
303+ const callArgs = configStub . setField . mock . calls [ 0 ] ;
304+ expect ( callArgs ?. [ 0 ] ) . toEqual ( {
305+ tenantId : "tenant-1" ,
306+ fieldPath : "payments.fee" ,
307+ value : { stringValue : "0.5%" } ,
308+ expectedChecksum : "abc123" ,
309+ } ) ;
310+ } ) ;
311+
312+ it ( "set() raises ChecksumMismatchError on ABORTED" , async ( ) => {
313+ configStub . setField . mockImplementation (
314+ ( _req : unknown , _meta : unknown , _opts : unknown , cb : ( ...args : unknown [ ] ) => void ) => {
315+ cb ( makeServiceError ( status . ABORTED , "checksum mismatch" ) ) ;
316+ } ,
317+ ) ;
318+
319+ await expect (
320+ client . set ( "tenant-1" , "payments.fee" , "0.5%" , { expectedChecksum : "stale" } ) ,
321+ ) . rejects . toThrow ( ChecksumMismatchError ) ;
322+ } ) ;
323+
324+ it ( "setMany() passes per-field expectedChecksums to proto" , async ( ) => {
325+ configStub . setFields . mockImplementation (
326+ ( _req : unknown , _meta : unknown , _opts : unknown , cb : ( ...args : unknown [ ] ) => void ) => {
327+ cb ( null , { configVersion : { version : 2 } } ) ;
328+ } ,
329+ ) ;
330+
331+ await client . setMany ( "tenant-1" , { a : "1" , b : "2" } , { expectedChecksums : { a : "cs-a" } } ) ;
332+
333+ const callArgs = configStub . setFields . mock . calls [ 0 ] ;
334+ const updates : Array < { fieldPath : string ; expectedChecksum ?: string } > =
335+ callArgs ?. [ 0 ] . updates ;
336+ const updateA = updates . find ( ( u ) => u . fieldPath === "a" ) ;
337+ const updateB = updates . find ( ( u ) => u . fieldPath === "b" ) ;
338+ expect ( updateA ?. expectedChecksum ) . toBe ( "cs-a" ) ;
339+ expect ( updateB ?. expectedChecksum ) . toBeUndefined ( ) ;
340+ } ) ;
341+
342+ it ( "setMany() raises ChecksumMismatchError on ABORTED" , async ( ) => {
343+ configStub . setFields . mockImplementation (
344+ ( _req : unknown , _meta : unknown , _opts : unknown , cb : ( ...args : unknown [ ] ) => void ) => {
345+ cb ( makeServiceError ( status . ABORTED , "checksum mismatch" ) ) ;
346+ } ,
347+ ) ;
348+
349+ await expect (
350+ client . setMany ( "tenant-1" , { a : "1" } , { expectedChecksums : { a : "stale" } } ) ,
351+ ) . rejects . toThrow ( ChecksumMismatchError ) ;
352+ } ) ;
353+
354+ it ( "setNull() passes expectedChecksum to proto" , async ( ) => {
355+ configStub . setField . mockImplementation (
356+ ( _req : unknown , _meta : unknown , _opts : unknown , cb : ( ...args : unknown [ ] ) => void ) => {
357+ cb ( null , { configVersion : { version : 3 } } ) ;
358+ } ,
359+ ) ;
360+
361+ await client . setNull ( "tenant-1" , "payments.fee" , { expectedChecksum : "xyz" } ) ;
362+
363+ const callArgs = configStub . setField . mock . calls [ 0 ] ;
364+ expect ( callArgs ?. [ 0 ] ) . toEqual ( {
365+ tenantId : "tenant-1" ,
366+ fieldPath : "payments.fee" ,
367+ value : undefined ,
368+ expectedChecksum : "xyz" ,
369+ } ) ;
370+ } ) ;
371+
372+ it ( "setNull() raises ChecksumMismatchError on ABORTED" , async ( ) => {
373+ configStub . setField . mockImplementation (
374+ ( _req : unknown , _meta : unknown , _opts : unknown , cb : ( ...args : unknown [ ] ) => void ) => {
375+ cb ( makeServiceError ( status . ABORTED , "checksum mismatch" ) ) ;
376+ } ,
377+ ) ;
378+
379+ await expect (
380+ client . setNull ( "tenant-1" , "payments.fee" , { expectedChecksum : "stale" } ) ,
381+ ) . rejects . toThrow ( ChecksumMismatchError ) ;
382+ } ) ;
383+ } ) ;
384+
291385 describe ( "serverInfo" , ( ) => {
292386 it ( "fetches and caches server info" , async ( ) => {
293387 serverStub . getServerInfo . mockImplementation (
0 commit comments