@@ -58,17 +58,23 @@ function makeItem(overrides: Partial<ChecklistItem> = {}): ChecklistItem {
5858 }
5959}
6060
61+ // Each test uses a unique houseId so module-level shared state doesn't leak
62+ // between tests. That is also what the production sharing guarantees — same
63+ // houseId → same state, different houseId → independent state.
64+ let houseCounter = 100
65+
6166describe ( 'useChecklists' , ( ) => {
6267 beforeEach ( ( ) => {
6368 vi . resetAllMocks ( )
69+ houseCounter ++
6470 } )
6571
6672 describe ( 'load' , ( ) => {
6773 it ( 'loads lists' , async ( ) => {
6874 const lists = [ makeList ( { id : 1 } ) , makeList ( { id : 2 } ) ]
6975 mockApi . listLists . mockResolvedValue ( lists )
7076
71- const c = useChecklists ( 1 )
77+ const c = useChecklists ( houseCounter )
7278 await c . load ( )
7379
7480 expect ( c . lists . value ) . toEqual ( lists )
@@ -79,11 +85,21 @@ describe('useChecklists', () => {
7985 it ( 'sets error on failure' , async ( ) => {
8086 mockApi . listLists . mockRejectedValue ( new Error ( 'fail' ) )
8187
82- const c = useChecklists ( 1 )
88+ const c = useChecklists ( houseCounter )
8389 await c . load ( )
8490
8591 expect ( c . error . value ) . toBe ( 'fail' )
8692 } )
93+
94+ it ( 'deduplicates concurrent calls for the same house' , async ( ) => {
95+ mockApi . listLists . mockResolvedValue ( [ makeList ( { id : 1 } ) ] )
96+
97+ const c = useChecklists ( houseCounter )
98+ const [ a , b ] = await Promise . all ( [ c . load ( ) , c . load ( ) ] )
99+
100+ expect ( a ) . toEqual ( b )
101+ expect ( mockApi . listLists ) . toHaveBeenCalledTimes ( 1 )
102+ } )
87103 } )
88104
89105 describe ( 'create' , ( ) => {
@@ -92,29 +108,95 @@ describe('useChecklists', () => {
92108 const newList = makeList ( { id : 10 } )
93109 mockApi . createList . mockResolvedValue ( newList )
94110
95- const c = useChecklists ( 1 )
111+ const c = useChecklists ( houseCounter )
96112 await c . load ( )
97113 const result = await c . create ( 'New' , 'desc' , 'cart' )
98114
99- expect ( mockApi . createList ) . toHaveBeenCalledWith ( 1 , 'New' , 'desc' , 'cart' )
115+ expect ( mockApi . createList ) . toHaveBeenCalledWith ( houseCounter , 'New' , 'desc' , 'cart' )
100116 expect ( result ) . toEqual ( newList )
101117 expect ( c . lists . value ) . toHaveLength ( 1 )
102118 } )
103119 } )
104120
121+ describe ( 'update' , ( ) => {
122+ it ( 'replaces the updated list in state' , async ( ) => {
123+ const original = makeList ( { id : 1 , name : 'Old' } )
124+ const updated = makeList ( { id : 1 , name : 'New' } )
125+ mockApi . listLists . mockResolvedValue ( [ original ] )
126+ mockApi . updateList . mockResolvedValue ( updated )
127+
128+ const c = useChecklists ( houseCounter )
129+ await c . load ( )
130+ await c . update ( 1 , { name : 'New' } )
131+
132+ expect ( c . lists . value [ 0 ] . name ) . toBe ( 'New' )
133+ } )
134+ } )
135+
105136 describe ( 'remove' , ( ) => {
106137 it ( 'removes list from state' , async ( ) => {
107138 mockApi . listLists . mockResolvedValue ( [ makeList ( { id : 1 } ) , makeList ( { id : 2 } ) ] )
108139 mockApi . deleteList . mockResolvedValue ( undefined )
109140
110- const c = useChecklists ( 1 )
141+ const c = useChecklists ( houseCounter )
111142 await c . load ( )
112143 await c . remove ( 1 )
113144
114145 expect ( c . lists . value ) . toHaveLength ( 1 )
115146 expect ( c . lists . value [ 0 ] . id ) . toBe ( 2 )
116147 } )
117148 } )
149+
150+ describe ( 'shared state' , ( ) => {
151+ it ( 'two callers for the same house share the same lists ref' , async ( ) => {
152+ const lists = [ makeList ( { id : 1 } ) , makeList ( { id : 2 } ) ]
153+ mockApi . listLists . mockResolvedValue ( lists )
154+
155+ const houseId = houseCounter
156+ const a = useChecklists ( houseId )
157+ const b = useChecklists ( houseId )
158+
159+ await a . load ( )
160+
161+ // Both consumers see the loaded lists even though only `a` triggered load.
162+ expect ( a . lists . value ) . toEqual ( lists )
163+ expect ( b . lists . value ) . toEqual ( lists )
164+ // And they reference the exact same ref instance.
165+ expect ( a . lists ) . toBe ( b . lists )
166+ } )
167+
168+ it ( 'propagates create across consumers for the same house' , async ( ) => {
169+ mockApi . listLists . mockResolvedValue ( [ ] )
170+ const newList = makeList ( { id : 10 , name : 'Shared' } )
171+ mockApi . createList . mockResolvedValue ( newList )
172+
173+ const houseId = houseCounter
174+ const a = useChecklists ( houseId )
175+ const b = useChecklists ( houseId )
176+
177+ await a . load ( )
178+ await a . create ( 'Shared' )
179+
180+ expect ( b . lists . value ) . toHaveLength ( 1 )
181+ expect ( b . lists . value [ 0 ] . id ) . toBe ( 10 )
182+ } )
183+
184+ it ( 'different house ids have independent state' , async ( ) => {
185+ mockApi . listLists . mockImplementation ( ( id : number ) =>
186+ Promise . resolve ( [ makeList ( { id : id * 100 } ) ] ) ,
187+ )
188+
189+ const a = useChecklists ( houseCounter )
190+ houseCounter ++
191+ const b = useChecklists ( houseCounter )
192+
193+ await a . load ( )
194+ await b . load ( )
195+
196+ expect ( a . lists . value ) . not . toEqual ( b . lists . value )
197+ expect ( a . lists . value [ 0 ] . id ) . not . toBe ( b . lists . value [ 0 ] . id )
198+ } )
199+ } )
118200} )
119201
120202describe ( 'useChecklistItems' , ( ) => {
0 commit comments