diff --git a/CHANGELOG.md b/CHANGELOG.md index b161347c..eb157491 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel ### Added +- Guestbooke: Added `editGuestbook` use case. - Guestbooks: Added `getGuestbookResponsesByGuestbookId` use case and repository support for retrieving paginated guestbook responses with total count as structured JSON. - Guestbooks: Added `downloadGuestbookResponsesByCollectionId` and `downloadGuestbookResponsesOfAGuestbook` use cases and repository support for exporting guestbook responses as raw CSV content. - Guestbooks: Added optional `includeStats` support to `getGuestbooksByCollectionId`, returning `usageCount` and `responseCount` when requested. diff --git a/docs/useCases.md b/docs/useCases.md index 5433e00a..82cc1b9a 100644 --- a/docs/useCases.md +++ b/docs/useCases.md @@ -143,6 +143,7 @@ The different use cases currently available in the package are classified below, - [Download Guestbook Responses Of A Guestbook](#download-guestbook-responses-of-a-guestbook) - [Guestbooks write use cases](#guestbooks-write-use-cases) - [Create a Guestbook](#create-a-guestbook) + - [Edit a Guestbook](#edit-a-guestbook) - [Set Guestbook Enabled](#set-guestbook-enabled) - [Assign Dataset Guestbook](#assign-dataset-guestbook) - [Remove Dataset Guestbook](#remove-dataset-guestbook) @@ -3172,6 +3173,7 @@ _See [use case](../src/guestbooks/domain/useCases/GetGuestbook.ts) implementatio Returns all [Guestbook](../src/guestbooks/domain/models/Guestbook.ts) entries available for a collection. Set `includeStats` to `true` to include `usageCount` and `responseCount` for each guestbook. +Set `includeInherited` to `true` to include the collection's guestbooks and guestbooks from the collection's hierarchical owners. ##### Example call: @@ -3180,9 +3182,10 @@ import { getGuestbooksByCollectionId } from '@iqss/dataverse-client-javascript' const collectionIdOrAlias = 'root' const includeStats = true +const includeInherited = true getGuestbooksByCollectionId - .execute(collectionIdOrAlias, includeStats) + .execute(collectionIdOrAlias, includeStats, includeInherited) .then((guestbooks: Guestbook[]) => { /* ... */ }) @@ -3290,6 +3293,27 @@ createGuestbook.execute(guestbook, collectionIdOrAlias).then(() => { _See [use case](../src/guestbooks/domain/useCases/CreateGuestbook.ts) implementation_. +#### Edit a Guestbook + +Edits an existing guestbook using [EditGuestbookDTO](../src/guestbooks/domain/dtos/EditGuestbookDTO.ts). + +##### Example call: + +```typescript +import { editGuestbook } from '@iqss/dataverse-client-javascript' + +const guestbookId = 123 +const guestbook: EditGuestbookDTO = { + name: 'new name' +} + +editGuestbook.execute(guestbookId, guestbook).then(() => { + /* ... */ +}) +``` + +_See [use case](../src/guestbooks/domain/useCases/EditGuestbook.ts) implementation_. + #### Set Guestbook Enabled Enables or disables a guestbook in a collection. diff --git a/src/guestbooks/domain/dtos/EditGuestbookDTO.ts b/src/guestbooks/domain/dtos/EditGuestbookDTO.ts new file mode 100644 index 00000000..edacaf8d --- /dev/null +++ b/src/guestbooks/domain/dtos/EditGuestbookDTO.ts @@ -0,0 +1,29 @@ +export type EditGuestbookQuestionTypeDTO = 'text' | 'textarea' | 'options' + +export interface EditGuestbookOptionDTO { + id?: number + value: string + displayOrder: number +} + +export interface EditGuestbookCustomQuestionDTO { + id?: number + question: string + required: boolean + displayOrder: number + type: EditGuestbookQuestionTypeDTO + hidden: boolean + optionValues?: EditGuestbookOptionDTO[] +} + +export interface EditGuestbookDTO { + id?: number + name: string + enabled: boolean + emailRequired: boolean + nameRequired: boolean + institutionRequired: boolean + positionRequired: boolean + createTime: string + customQuestions?: EditGuestbookCustomQuestionDTO[] +} diff --git a/src/guestbooks/domain/models/Guestbook.ts b/src/guestbooks/domain/models/Guestbook.ts index 595e801e..899ef793 100644 --- a/src/guestbooks/domain/models/Guestbook.ts +++ b/src/guestbooks/domain/models/Guestbook.ts @@ -1,11 +1,13 @@ export type GuestbookQuestionType = 'text' | 'textarea' | 'options' export interface GuestbookOption { + id?: number value: string displayOrder: number } export interface GuestbookCustomQuestion { + id?: number question: string required: boolean displayOrder: number diff --git a/src/guestbooks/domain/repositories/IGuestbooksRepository.ts b/src/guestbooks/domain/repositories/IGuestbooksRepository.ts index 3030457c..38fbb590 100644 --- a/src/guestbooks/domain/repositories/IGuestbooksRepository.ts +++ b/src/guestbooks/domain/repositories/IGuestbooksRepository.ts @@ -1,4 +1,5 @@ import { CreateGuestbookDTO } from '../dtos/CreateGuestbookDTO' +import { EditGuestbookDTO } from '../dtos/EditGuestbookDTO' import { Guestbook } from '../models/Guestbook' import { GuestbookResponseSubset } from '../models/GuestbookResponse' @@ -7,6 +8,7 @@ export interface IGuestbooksRepository { collectionIdOrAlias: number | string, guestbook: CreateGuestbookDTO ): Promise + editGuestbook(guestbookId: number, guestbook: EditGuestbookDTO): Promise getGuestbook(guestbookId: number): Promise getGuestbooksByCollectionId( collectionIdOrAlias: number | string, diff --git a/src/guestbooks/domain/useCases/EditGuestbook.ts b/src/guestbooks/domain/useCases/EditGuestbook.ts new file mode 100644 index 00000000..a1b267f5 --- /dev/null +++ b/src/guestbooks/domain/useCases/EditGuestbook.ts @@ -0,0 +1,17 @@ +import { EditGuestbookDTO } from '../dtos/EditGuestbookDTO' +import { IGuestbooksRepository } from '../repositories/IGuestbooksRepository' + +export class EditGuestbook { + constructor(private readonly guestbooksRepository: IGuestbooksRepository) {} + + /** + * Edits an existing guestbook. + * + * @param {number} guestbookId - Guestbook identifier. + * @param {EditGuestbookDTO} guestbook - Guestbook edit payload. + * @returns {Promise} + */ + async execute(guestbookId: number, guestbook: EditGuestbookDTO): Promise { + return await this.guestbooksRepository.editGuestbook(guestbookId, guestbook) + } +} diff --git a/src/guestbooks/index.ts b/src/guestbooks/index.ts index 7c5bdd0a..cd1fbe6f 100644 --- a/src/guestbooks/index.ts +++ b/src/guestbooks/index.ts @@ -1,5 +1,6 @@ import { GuestbooksRepository } from './infra/repositories/GuestbooksRepository' import { CreateGuestbook } from './domain/useCases/CreateGuestbook' +import { EditGuestbook } from './domain/useCases/EditGuestbook' import { DownloadGuestbookResponsesByCollectionId } from './domain/useCases/DownloadGuestbookResponsesByCollectionId' import { DownloadGuestbookResponsesOfAGuestbook } from './domain/useCases/DownloadGuestbookResponsesOfAGuestbook' import { GetGuestbook } from './domain/useCases/GetGuestbook' @@ -12,6 +13,7 @@ import { RemoveDatasetGuestbook } from './domain/useCases/RemoveDatasetGuestbook const guestbooksRepository = new GuestbooksRepository() const createGuestbook = new CreateGuestbook(guestbooksRepository) +const editGuestbook = new EditGuestbook(guestbooksRepository) const downloadGuestbookResponsesByCollectionId = new DownloadGuestbookResponsesByCollectionId( guestbooksRepository ) @@ -29,6 +31,7 @@ const removeDatasetGuestbook = new RemoveDatasetGuestbook(guestbooksRepository) export { createGuestbook, + editGuestbook, downloadGuestbookResponsesByCollectionId, downloadGuestbookResponsesOfAGuestbook, getGuestbook, @@ -44,6 +47,11 @@ export { CreateGuestbookCustomQuestionDTO, CreateGuestbookOptionDTO } from './domain/dtos/CreateGuestbookDTO' +export { + EditGuestbookDTO, + EditGuestbookCustomQuestionDTO, + EditGuestbookOptionDTO +} from './domain/dtos/EditGuestbookDTO' export { GuestbookResponsesDTO, GuestbookResponsesPaginationDTO diff --git a/src/guestbooks/infra/repositories/GuestbooksRepository.ts b/src/guestbooks/infra/repositories/GuestbooksRepository.ts index 6112accd..30ed988c 100644 --- a/src/guestbooks/infra/repositories/GuestbooksRepository.ts +++ b/src/guestbooks/infra/repositories/GuestbooksRepository.ts @@ -1,5 +1,6 @@ import { ApiRepository } from '../../../core/infra/repositories/ApiRepository' import { CreateGuestbookDTO } from '../../domain/dtos/CreateGuestbookDTO' +import { EditGuestbookDTO } from '../../domain/dtos/EditGuestbookDTO' import { GuestbookResponsesDTO } from '../../domain/dtos/GuestbookResponsesDTO' import { Guestbook } from '../../domain/models/Guestbook' import { GuestbookResponseSubset } from '../../domain/models/GuestbookResponse' @@ -24,6 +25,17 @@ export class GuestbooksRepository extends ApiRepository implements IGuestbooksRe }) } + public async editGuestbook(guestbookId: number, guestbook: EditGuestbookDTO): Promise { + return this.doPut( + this.buildApiEndpoint(this.guestbooksResourceName, undefined, guestbookId), + guestbook + ) + .then(() => undefined) + .catch((error) => { + throw error + }) + } + public async getGuestbook(guestbookId: number): Promise { return this.doGet( this.buildApiEndpoint(this.guestbooksResourceName, undefined, guestbookId), diff --git a/test/integration/guestbooks/GuestbooksRepository.test.ts b/test/integration/guestbooks/GuestbooksRepository.test.ts index 1f503178..8e90719c 100644 --- a/test/integration/guestbooks/GuestbooksRepository.test.ts +++ b/test/integration/guestbooks/GuestbooksRepository.test.ts @@ -2,6 +2,7 @@ import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ import { ApiConfig, ReadError, WriteError } from '../../../src' import { GuestbooksRepository } from '../../../src/guestbooks/infra/repositories/GuestbooksRepository' import { CreateGuestbookDTO } from '../../../src/guestbooks/domain/dtos/CreateGuestbookDTO' +import { EditGuestbookDTO } from '../../../src/guestbooks/domain/dtos/EditGuestbookDTO' import { TestConstants } from '../../testHelpers/TestConstants' import { createDataset, @@ -378,6 +379,248 @@ describe('GuestbooksRepository', () => { }) }) + describe('editGuestbook', () => { + const buildEditGuestbookDTO = ( + currentGuestbook: Awaited>, + overrides: Partial + ): EditGuestbookDTO => ({ + name: currentGuestbook.name, + enabled: currentGuestbook.enabled, + emailRequired: currentGuestbook.emailRequired, + nameRequired: currentGuestbook.nameRequired, + institutionRequired: currentGuestbook.institutionRequired, + positionRequired: currentGuestbook.positionRequired, + createTime: currentGuestbook.createTime, + customQuestions: currentGuestbook.customQuestions ?? [], + ...overrides + }) + + test('should update existing custom question when custom question id is provided', async () => { + const uniqueSuffix = Date.now().toString() + const guestbookId = await sut.createGuestbook(testCollectionId, { + ...createGuestbookDTO, + name: `guestbook edit custom question with id ${uniqueSuffix}` + }) + const guestbookBeforeEdit = await sut.getGuestbook(guestbookId) + const existingQuestion = guestbookBeforeEdit.customQuestions[0] + + const editedGuestbook = buildEditGuestbookDTO(guestbookBeforeEdit, { + name: `edited custom question with id guestbook ${uniqueSuffix}`, + customQuestions: [ + { + id: existingQuestion.id, + question: `updated custom question ${uniqueSuffix}`, + required: false, + displayOrder: 0, + type: 'textarea', + hidden: false + } + ] + }) + + await sut.editGuestbook(guestbookId, editedGuestbook) + + const actual = await sut.getGuestbook(guestbookId) + expect(actual.name).toBe(editedGuestbook.name) + expect(actual.customQuestions).toHaveLength(1) + expect(actual.customQuestions[0]).toEqual( + expect.objectContaining({ + id: existingQuestion.id, + question: editedGuestbook.customQuestions?.[0].question, + required: false, + displayOrder: 0, + type: 'textarea', + hidden: false + }) + ) + }) + + test('should create replacement custom question when custom question id is not provided', async () => { + const uniqueSuffix = Date.now().toString() + const guestbookId = await sut.createGuestbook(testCollectionId, { + ...createGuestbookDTO, + name: `guestbook edit custom question without id ${uniqueSuffix}` + }) + const guestbookBeforeEdit = await sut.getGuestbook(guestbookId) + const existingQuestion = guestbookBeforeEdit.customQuestions[0] + + const editedGuestbook = buildEditGuestbookDTO(guestbookBeforeEdit, { + name: `edited custom question without id guestbook ${uniqueSuffix}`, + customQuestions: [ + { + question: `replacement custom question ${uniqueSuffix}`, + required: false, + displayOrder: 0, + type: 'textarea', + hidden: false + } + ] + }) + + await sut.editGuestbook(guestbookId, editedGuestbook) + + const actual = await sut.getGuestbook(guestbookId) + expect(actual.name).toBe(editedGuestbook.name) + expect(actual.customQuestions).toHaveLength(1) + expect(actual.customQuestions[0]).toEqual( + expect.objectContaining({ + question: editedGuestbook.customQuestions?.[0].question, + required: false, + displayOrder: 0, + type: 'textarea', + hidden: false + }) + ) + expect(actual.customQuestions[0].id).toBeDefined() + expect(actual.customQuestions[0].id).not.toBe(existingQuestion.id) + }) + + test('should edit guestbook without custom questions', async () => { + const uniqueSuffix = Date.now().toString() + const guestbookId = await sut.createGuestbook(testCollectionId, { + ...createGuestbookDTO, + name: `guestbook edit no questions ${uniqueSuffix}`, + enabled: false, + emailRequired: false, + nameRequired: false, + customQuestions: [] + }) + const guestbookBeforeEdit = await sut.getGuestbook(guestbookId) + + const editedGuestbook = buildEditGuestbookDTO(guestbookBeforeEdit, { + name: `edited guestbook ${uniqueSuffix}`, + enabled: true, + emailRequired: true, + nameRequired: true, + institutionRequired: true, + positionRequired: false, + customQuestions: [] + }) + + await sut.editGuestbook(guestbookId, editedGuestbook) + + const actual = await sut.getGuestbook(guestbookId) + expect(actual.name).toBe(editedGuestbook.name) + expect(actual.enabled).toBe(true) + expect(actual.emailRequired).toBe(true) + expect(actual.nameRequired).toBe(true) + expect(actual.institutionRequired).toBe(true) + expect(actual.positionRequired).toBe(false) + expect(actual.customQuestions ?? []).toHaveLength(0) + }) + + test('should edit guestbook with a textarea custom question', async () => { + const uniqueSuffix = Date.now().toString() + const guestbookId = await sut.createGuestbook(testCollectionId, { + ...createGuestbookDTO, + name: `guestbook edit textarea ${uniqueSuffix}`, + customQuestions: [] + }) + const guestbookBeforeEdit = await sut.getGuestbook(guestbookId) + + const editedGuestbook = buildEditGuestbookDTO(guestbookBeforeEdit, { + name: `edited textarea guestbook ${uniqueSuffix}`, + customQuestions: [ + { + question: `textarea question ${uniqueSuffix}`, + required: false, + displayOrder: 0, + type: 'textarea', + hidden: false + } + ] + }) + + await sut.editGuestbook(guestbookId, editedGuestbook) + + const actual = await sut.getGuestbook(guestbookId) + expect(actual.name).toBe(editedGuestbook.name) + expect(actual.customQuestions).toHaveLength(1) + expect(actual.customQuestions[0]).toEqual( + expect.objectContaining({ + question: editedGuestbook.customQuestions?.[0].question, + required: false, + displayOrder: 0, + type: 'textarea', + hidden: false + }) + ) + }) + + test('should edit guestbook with an options custom question', async () => { + const uniqueSuffix = Date.now().toString() + const guestbookId = await sut.createGuestbook(testCollectionId, { + ...createGuestbookDTO, + name: `guestbook edit options ${uniqueSuffix}`, + customQuestions: [] + }) + const guestbookBeforeEdit = await sut.getGuestbook(guestbookId) + + const editedGuestbook = buildEditGuestbookDTO(guestbookBeforeEdit, { + name: `edited options guestbook ${uniqueSuffix}`, + customQuestions: [ + { + question: `options question ${uniqueSuffix}`, + required: true, + displayOrder: 0, + type: 'options', + hidden: false, + optionValues: [ + { value: 'Red', displayOrder: 0 }, + { value: 'Blue', displayOrder: 1 } + ] + } + ] + }) + + await sut.editGuestbook(guestbookId, editedGuestbook) + + const actual = await sut.getGuestbook(guestbookId) + expect(actual.name).toBe(editedGuestbook.name) + expect(actual.customQuestions).toHaveLength(1) + expect(actual.customQuestions[0]).toEqual( + expect.objectContaining({ + question: editedGuestbook.customQuestions?.[0].question, + required: true, + displayOrder: 0, + type: 'options', + hidden: false, + optionValues: expect.arrayContaining([ + expect.objectContaining({ value: 'Red', displayOrder: 0 }), + expect.objectContaining({ value: 'Blue', displayOrder: 1 }) + ]) + }) + ) + }) + + test('should remove custom questions when custom question field is empty', async () => { + const uniqueSuffix = Date.now().toString() + const guestbookId = await sut.createGuestbook(testCollectionId, { + ...createGuestbookDTO, + name: `guestbook edit empty field ${uniqueSuffix}` + }) + const guestbookBeforeEdit = await sut.getGuestbook(guestbookId) + expect(guestbookBeforeEdit.customQuestions).toHaveLength(3) + + const editedGuestbook = buildEditGuestbookDTO(guestbookBeforeEdit, { + name: `edited empty field guestbook ${uniqueSuffix}`, + customQuestions: [] + }) + + await sut.editGuestbook(guestbookId, editedGuestbook) + + const actual = await sut.getGuestbook(guestbookId) + expect(actual.name).toBe(editedGuestbook.name) + expect(actual.customQuestions ?? []).toHaveLength(0) + }) + + test('should return error when guestbook does not exist', async () => { + await expect( + sut.editGuestbook(999999, { ...createGuestbookDTO, createTime: '2026-06-12T00:00:00Z' }) + ).rejects.toThrow(WriteError) + }) + }) + describe('getGuestbookResponsesByGuestbookId', () => { test('should return responses for one guestbook', async () => { const setup = await createGuestbookDownloadSetup('guestbook responses endpoint test') diff --git a/test/unit/guestbooks/EditGuestbook.test.ts b/test/unit/guestbooks/EditGuestbook.test.ts new file mode 100644 index 00000000..4ac358ec --- /dev/null +++ b/test/unit/guestbooks/EditGuestbook.test.ts @@ -0,0 +1,56 @@ +import { WriteError } from '../../../src' +import { EditGuestbookDTO } from '../../../src/guestbooks/domain/dtos/EditGuestbookDTO' +import { IGuestbooksRepository } from '../../../src/guestbooks/domain/repositories/IGuestbooksRepository' +import { EditGuestbook } from '../../../src/guestbooks/domain/useCases/EditGuestbook' + +describe('EditGuestbook', () => { + const editGuestbookDTO: EditGuestbookDTO = { + name: 'my edited guestbook', + enabled: true, + emailRequired: true, + nameRequired: true, + institutionRequired: false, + positionRequired: false, + createTime: '2026-06-12T00:00:00Z', + customQuestions: [ + { + id: 1, + question: "how's your day", + required: true, + displayOrder: 0, + type: 'text', + hidden: false + }, + { + question: 'What color car do you drive', + required: true, + displayOrder: 1, + type: 'options', + hidden: false, + optionValues: [ + { id: 10, value: 'Red', displayOrder: 0 }, + { value: 'White', displayOrder: 1 } + ] + } + ] + } + + test('should edit guestbook', async () => { + const repository: IGuestbooksRepository = {} as IGuestbooksRepository + repository.editGuestbook = jest.fn().mockResolvedValue(undefined) + + const sut = new EditGuestbook(repository) + const actual = await sut.execute(123, editGuestbookDTO) + + expect(repository.editGuestbook).toHaveBeenCalledWith(123, editGuestbookDTO) + expect(actual).toBeUndefined() + }) + + test('should throw WriteError when repository fails', async () => { + const repository: IGuestbooksRepository = {} as IGuestbooksRepository + repository.editGuestbook = jest.fn().mockRejectedValue(new WriteError()) + const sut = new EditGuestbook(repository) + + await expect(sut.execute(123, editGuestbookDTO)).rejects.toThrow(WriteError) + }) +}) diff --git a/test/unit/guestbooks/GuestbooksRepository.test.ts b/test/unit/guestbooks/GuestbooksRepository.test.ts index f820254d..5a4084f0 100644 --- a/test/unit/guestbooks/GuestbooksRepository.test.ts +++ b/test/unit/guestbooks/GuestbooksRepository.test.ts @@ -7,6 +7,7 @@ import { GuestbooksRepository } from '../../../src/guestbooks/infra/repositories import { ReadError } from '../../../src/core/domain/repositories/ReadError' import { TestConstants } from '../../testHelpers/TestConstants' import { EventType } from '../../../src/guestbooks/domain/models/GuestbookResponse' +import { EditGuestbookDTO } from '../../../src/guestbooks/domain/dtos/EditGuestbookDTO' describe('GuestbooksRepository', () => { const sut = new GuestbooksRepository() @@ -84,6 +85,36 @@ describe('GuestbooksRepository', () => { } const guestbookResponsesCsv = 'Guestbook,Dataset,Dataset PID,Date,Type,File Name,File Id,File PID,User Name,Email,Institution,Position,Custom Questions' + const editGuestbookDTO: EditGuestbookDTO = { + name: 'edited test', + enabled: true, + emailRequired: true, + nameRequired: true, + institutionRequired: false, + positionRequired: false, + createTime: '2026-06-12T00:00:00Z', + customQuestions: [ + { + id: 1, + question: "how's your day", + required: true, + displayOrder: 0, + type: 'text', + hidden: false + }, + { + question: 'What color car do you drive', + required: true, + displayOrder: 1, + type: 'options', + hidden: false, + optionValues: [ + { id: 10, value: 'Red', displayOrder: 0 }, + { value: 'White', displayOrder: 1 } + ] + } + ] + } beforeEach(() => { ApiConfig.init( @@ -128,6 +159,41 @@ describe('GuestbooksRepository', () => { expect(actual[0].responseCount).toBe(2) }) + test('should list guestbooks with inherited guestbooks when includeInherited is true', async () => { + jest.spyOn(axios, 'get').mockResolvedValue(guestbooksResponseWithoutStats) + + const actual = await sut.getGuestbooksByCollectionId(collectionIdOrAlias, false, true) + + expect(axios.get).toHaveBeenCalledWith( + `${TestConstants.TEST_API_URL}/guestbooks/${collectionIdOrAlias}/list`, + { + params: { + includeInherited: true + }, + headers: TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY.headers + } + ) + expect(actual).toStrictEqual(guestbooksResponseWithoutStats.data.data) + }) + + test('should list guestbooks with stats and inherited guestbooks when both options are true', async () => { + jest.spyOn(axios, 'get').mockResolvedValue(guestbooksResponse) + + const actual = await sut.getGuestbooksByCollectionId(collectionIdOrAlias, true, true) + + expect(axios.get).toHaveBeenCalledWith( + `${TestConstants.TEST_API_URL}/guestbooks/${collectionIdOrAlias}/list`, + { + params: { + includeStats: true, + includeInherited: true + }, + headers: TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY.headers + } + ) + expect(actual).toStrictEqual(guestbooksResponse.data.data) + }) + test('should return error result on error response', async () => { jest.spyOn(axios, 'get').mockRejectedValue(TestConstants.TEST_ERROR_RESPONSE) @@ -137,6 +203,33 @@ describe('GuestbooksRepository', () => { }) }) + describe('editGuestbook', () => { + test('should edit guestbook', async () => { + jest.spyOn(axios, 'put').mockResolvedValue({ data: { status: 'OK' } }) + + const actual = await sut.editGuestbook(12, editGuestbookDTO) + + expect(axios.put).toHaveBeenCalledWith( + `${TestConstants.TEST_API_URL}/guestbooks/12`, + JSON.stringify(editGuestbookDTO), + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY + ) + const requestPayload = JSON.parse((axios.put as jest.Mock).mock.calls[0][1] as string) as + | EditGuestbookDTO + | undefined + expect(requestPayload?.id).toBeUndefined() + expect(requestPayload?.customQuestions?.[0].id).toBe(1) + expect(requestPayload?.customQuestions?.[1].optionValues?.[0].id).toBe(10) + expect(actual).toBeUndefined() + }) + + test('should return error result on error response', async () => { + jest.spyOn(axios, 'put').mockRejectedValue(TestConstants.TEST_ERROR_RESPONSE) + + await expect(sut.editGuestbook(12, editGuestbookDTO)).rejects.toThrow() + }) + }) + describe('getGuestbookResponsesByGuestbookId', () => { test('should list guestbook responses for a guestbook', async () => { jest.spyOn(axios, 'get').mockResolvedValue(guestbookResponsesOfAGuestbookResponse)