1+ import { InsertLetter , Letter , UpdateLetter , UpsertLetter } from "../types" ;
12import { createTables , DBContext , deleteTables , setupDynamoDBContainer } from './db' ;
23import { LetterRepository } from '../letter-repository' ;
3- import { Letter } from '../types' ;
44import { Logger } from 'pino' ;
55import { createTestLogger , LogStream } from './logs' ;
66import { PutCommand } from '@aws-sdk/lib-dynamodb' ;
7- import { LetterDto } from '../../../../lambdas/api-handler/src/contracts/letters' ;
87
9- function createLetter ( supplierId : string , letterId : string , status : Letter [ 'status' ] = 'PENDING' ) : Omit < Letter , 'ttl' | 'supplierStatus' | 'supplierStatusSk' > {
8+ function createLetter (
9+ supplierId : string ,
10+ letterId : string ,
11+ status : Letter [ "status" ] = "PENDING" ,
12+ ) : InsertLetter {
1013 return {
1114 id : letterId ,
1215 supplierId : supplierId ,
@@ -107,14 +110,14 @@ describe('LetterRepository', () => {
107110 await letterRepository . putLetter ( letter ) ;
108111 await checkLetterStatus ( 'supplier1' , 'letter1' , 'PENDING' ) ;
109112
110- const letterDto : LetterDto = {
113+ const updateLetter : UpdateLetter = {
111114 id : 'letter1' ,
112115 supplierId : 'supplier1' ,
113116 status : 'REJECTED' ,
114117 reasonCode : 'R01' ,
115118 reasonText : 'Reason text'
116119 } ;
117- await letterRepository . updateLetterStatus ( letterDto ) ;
120+ await letterRepository . updateLetterStatus ( updateLetter ) ;
118121
119122 const updatedLetter = await letterRepository . getLetterById ( 'supplier1' , 'letter1' ) ;
120123 expect ( updatedLetter . status ) . toBe ( 'REJECTED' ) ;
@@ -132,7 +135,7 @@ describe('LetterRepository', () => {
132135 // Month is zero-indexed in JavaScript Date
133136 // Day is one-indexed
134137 jest . setSystemTime ( new Date ( 2020 , 1 , 2 ) ) ;
135- const letterDto : LetterDto = {
138+ const letterDto : UpdateLetter = {
136139 id : 'letter1' ,
137140 supplierId : 'supplier1' ,
138141 status : 'DELIVERED'
@@ -145,7 +148,7 @@ describe('LetterRepository', () => {
145148 } ) ;
146149
147150 test ( 'can\'t update a letter that does not exist' , async ( ) => {
148- const letterDto : LetterDto = {
151+ const letterDto : UpdateLetter = {
149152 id : 'letter1' ,
150153 supplierId : 'supplier1' ,
151154 status : 'DELIVERED'
@@ -160,7 +163,7 @@ describe('LetterRepository', () => {
160163 lettersTableName : 'nonexistent-table'
161164 } ) ;
162165
163- const letterDto : LetterDto = {
166+ const letterDto : UpdateLetter = {
164167 id : 'letter1' ,
165168 supplierId : 'supplier1' ,
166169 status : 'DELIVERED'
@@ -190,7 +193,7 @@ describe('LetterRepository', () => {
190193 const pendingLetters = await letterRepository . getLettersByStatus ( 'supplier1' , 'PENDING' ) ;
191194 expect ( pendingLetters . letters ) . toHaveLength ( 2 ) ;
192195
193- const letterDto : LetterDto = {
196+ const letterDto : UpdateLetter = {
194197 id : 'letter1' ,
195198 supplierId : 'supplier1' ,
196199 status : 'DELIVERED'
@@ -367,4 +370,119 @@ describe('LetterRepository', () => {
367370 await expect ( misconfiguredRepository . putLetterBatch ( [ createLetter ( 'supplier1' , 'letter1' ) ] ) )
368371 . rejects . toThrow ( 'Cannot do operations on a non-existent table' ) ;
369372 } ) ;
373+
374+ test ( "successful upsert (update status) returns updated letter" , async ( ) => {
375+ const insertLetter : InsertLetter = createLetter ( "supplier1" , "letter1" ) ;
376+
377+ const existingLetter = await letterRepository . putLetter ( insertLetter ) ;
378+
379+ const result = await letterRepository . upsertLetter ( {
380+ id : "letter1" ,
381+ supplierId : "supplier1" ,
382+ status : "REJECTED" ,
383+ reasonCode : "R01" ,
384+ reasonText : "R01 text" ,
385+ } ) ;
386+
387+ expect ( result ) . toEqual (
388+ expect . objectContaining ( {
389+ id : "letter1" ,
390+ status : "REJECTED" ,
391+ specificationId : "specification1" ,
392+ groupId : "group1" ,
393+ reasonCode : "R01" ,
394+ reasonText : "R01 text" ,
395+ supplierId : "supplier1" ,
396+ url : "s3://bucket/letter1.pdf" ,
397+ supplierStatus : "supplier1#REJECTED" ,
398+ } ) ,
399+ ) ;
400+ expect ( Date . parse ( result . updatedAt ) ) . toBeGreaterThan (
401+ Date . parse ( existingLetter . updatedAt ) ,
402+ ) ;
403+ expect ( Date . parse ( result . updatedAt ) ) . toBeLessThan ( Date . now ( ) ) ;
404+ expect ( result . createdAt ) . toBe ( existingLetter . createdAt ) ;
405+ expect ( result . createdAt ) . toBe ( result . supplierStatusSk ) ;
406+ expect ( result . ttl ) . toBe ( existingLetter . ttl ) ;
407+ } ) ;
408+
409+ test ( "successful upsert (insert) returns created letter" , async ( ) => {
410+ const upsertLetter : UpsertLetter = {
411+ id : "letter1" ,
412+ status : "PENDING" ,
413+ specificationId : "specification1" ,
414+ groupId : "group1" ,
415+ supplierId : "supplier1" ,
416+ url : "s3://bucket/letter1.pdf" ,
417+ } ;
418+
419+ const beforeInsert = Date . now ( ) - 1 ; // widen window
420+
421+ const result = await letterRepository . upsertLetter ( upsertLetter ) ;
422+
423+ expect ( result ) . toEqual (
424+ expect . objectContaining ( {
425+ id : "letter1" ,
426+ status : "PENDING" ,
427+ specificationId : "specification1" ,
428+ groupId : "group1" ,
429+ supplierId : "supplier1" ,
430+ url : "s3://bucket/letter1.pdf" ,
431+ } ) ,
432+ ) ;
433+
434+ expect ( Date . parse ( result . updatedAt ) ) . toBeGreaterThan ( beforeInsert ) ;
435+ expect ( Date . parse ( result . updatedAt ) ) . toBeLessThan ( Date . now ( ) ) ;
436+ expect ( result . createdAt ) . toBe ( result . updatedAt ) ;
437+ expect ( result . supplierStatusSk ) . toBe ( result . createdAt ) ;
438+ } ) ;
439+
440+ test ( "unsuccessful upsert should throw error" , async ( ) => {
441+ const mockSend = jest . fn ( ) . mockResolvedValue ( { Items : null } ) ;
442+ const mockDdbClient = { send : mockSend } as any ;
443+ const repo = new LetterRepository (
444+ mockDdbClient ,
445+ { debug : jest . fn ( ) } as any ,
446+ { lettersTableName : "letters" , lettersTtlHours : 1 } ,
447+ ) ;
448+
449+ await expect (
450+ repo . upsertLetter ( {
451+ id : "letter1" ,
452+ status : "PENDING" ,
453+ supplierId : "supplier1" ,
454+ } ) ,
455+ ) . rejects . toThrow ( "upsertLetter: no attributes returned" ) ;
456+ } ) ;
457+
458+ test ( "successful upsert without status" , async ( ) => {
459+ const insertLetter : InsertLetter = createLetter ( "supplier1" , "letter1" ) ;
460+
461+ const existingLetter = await letterRepository . putLetter ( insertLetter ) ;
462+
463+ const result = await letterRepository . upsertLetter ( {
464+ id : "letter1" ,
465+ supplierId : "supplier1" ,
466+ url : "s3://updatedBucket/letter1.pdf" ,
467+ } ) ;
468+
469+ expect ( result ) . toEqual (
470+ expect . objectContaining ( {
471+ id : "letter1" ,
472+ status : "PENDING" ,
473+ specificationId : "specification1" ,
474+ groupId : "group1" ,
475+ supplierId : "supplier1" ,
476+ url : "s3://updatedBucket/letter1.pdf" ,
477+ supplierStatus : "supplier1#PENDING" ,
478+ } ) ,
479+ ) ;
480+ expect ( Date . parse ( result . updatedAt ) ) . toBeGreaterThan (
481+ Date . parse ( existingLetter . updatedAt ) ,
482+ ) ;
483+ expect ( Date . parse ( result . updatedAt ) ) . toBeLessThan ( Date . now ( ) ) ;
484+ expect ( result . createdAt ) . toBe ( existingLetter . createdAt ) ;
485+ expect ( result . createdAt ) . toBe ( result . supplierStatusSk ) ;
486+ expect ( result . ttl ) . toBe ( existingLetter . ttl ) ;
487+ } ) ;
370488} ) ;
0 commit comments