Bugzilla – Attachment 152942 Details for
Bug 30708
Creation of a new 'Preservation' module
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 30708: Add tests
Bug-30708-Add-tests.patch (text/plain), 79.95 KB, created by
Jonathan Druart
on 2023-07-03 13:29:07 UTC
(
hide
)
Description:
Bug 30708: Add tests
Filename:
MIME Type:
Creator:
Jonathan Druart
Created:
2023-07-03 13:29:07 UTC
Size:
79.95 KB
patch
obsolete
>From b165fc2ebd2099fd0a6c5f3badd1ed09c52f9ee6 Mon Sep 17 00:00:00 2001 >From: Jonathan Druart <jonathan.druart@bugs.koha-community.org> >Date: Fri, 7 Apr 2023 14:48:19 +0200 >Subject: [PATCH] Bug 30708: Add tests > >Sponsored-by: BULAC - http://www.bulac.fr/ > >Signed-off-by: BULAC - http://www.bulac.fr/ >--- > .../integration/Preservation/Settings.ts | 254 ++++++++ > t/cypress/integration/Preservation/Trains.ts | 593 ++++++++++++++++++ > .../integration/Preservation/WaitingList.ts | 185 ++++++ > .../Koha/Preservation/Processings.t | 58 ++ > t/db_dependent/Koha/Preservation/Trains.t | 102 +++ > .../api/v1/preservation_Processings.t | 369 +++++++++++ > t/db_dependent/api/v1/preservation_Trains.t | 525 ++++++++++++++++ > .../api/v1/preservation_WaitingList.t | 167 +++++ > 8 files changed, 2253 insertions(+) > create mode 100644 t/cypress/integration/Preservation/Settings.ts > create mode 100644 t/cypress/integration/Preservation/Trains.ts > create mode 100644 t/cypress/integration/Preservation/WaitingList.ts > create mode 100755 t/db_dependent/Koha/Preservation/Processings.t > create mode 100755 t/db_dependent/Koha/Preservation/Trains.t > create mode 100755 t/db_dependent/api/v1/preservation_Processings.t > create mode 100755 t/db_dependent/api/v1/preservation_Trains.t > create mode 100755 t/db_dependent/api/v1/preservation_WaitingList.t > >diff --git a/t/cypress/integration/Preservation/Settings.ts b/t/cypress/integration/Preservation/Settings.ts >new file mode 100644 >index 00000000000..e72d4b5858b >--- /dev/null >+++ b/t/cypress/integration/Preservation/Settings.ts >@@ -0,0 +1,254 @@ >+import { mount } from "@cypress/vue"; >+ >+function get_attributes() { >+ return [ >+ { >+ processing_attribute_id: 1, >+ processing_id: 1, >+ name: "Country", >+ type: "authorised_value", >+ option_source: "COUNTRY", >+ }, >+ { >+ processing_attribute_id: 1, >+ processing_id: 1, >+ name: "DB", >+ type: "db_column", >+ option_source: "biblio.title", >+ }, >+ { >+ processing_attribute_id: 1, >+ processing_id: 1, >+ name: "Height", >+ type: "free_text", >+ option_source: null, >+ }, >+ ]; >+} >+function get_processing() { >+ return { >+ name: "test processing", >+ processing_id: 1, >+ attributes: get_attributes(), >+ }; >+} >+describe("Processings", () => { >+ beforeEach(() => { >+ cy.login(); >+ cy.title().should("eq", "Koha staff interface"); >+ cy.intercept( >+ "GET", >+ "/cgi-bin/koha/svc/config/systempreferences/?pref=PreservationModule", >+ '{"value":"1"}' >+ ); >+ cy.intercept( >+ "GET", >+ "/cgi-bin/koha/svc/config/systempreferences/?pref=PreservationNotForLoanWaitingListIn", >+ '{"value":"24"}' >+ ); >+ cy.intercept( >+ "GET", >+ "/cgi-bin/koha/svc/config/systempreferences/?pref=PreservationNotForLoanDefaultTrainIn", >+ '{"value":"42"}' >+ ); >+ cy.intercept( >+ "GET", >+ "/api/v1/authorised_value_categories/NOT_LOAN/authorised_values", >+ [ >+ { >+ category_name: "NOT_LOAN", >+ description: "Ordered", >+ value: "-1", >+ }, >+ { >+ category_name: "NOT_LOAN", >+ description: "Not for loan", >+ value: "1", >+ }, >+ { >+ category_name: "NOT_LOAN", >+ description: "Staff collection", >+ value: "2", >+ }, >+ { >+ category_name: "NOT_LOAN", >+ description: "Added to bundle", >+ value: "3", >+ }, >+ { >+ category_name: "NOT_LOAN", >+ description: "In preservation", >+ value: "24", >+ }, >+ { >+ category_name: "NOT_LOAN", >+ description: "In preservation external", >+ value: "42", >+ }, >+ ] >+ ); >+ }); >+ >+ it("Settings", () => { >+ cy.visit("/cgi-bin/koha/preservation/home.pl"); >+ cy.get("#navmenulist").contains("Settings").click(); >+ cy.get("#not_for_loan_waiting_list_in .vs__selected").contains( >+ "In preservation" >+ ); >+ cy.get("#not_for_loan_default_train_in .vs__selected").contains( >+ "In preservation external" >+ ); >+ }); >+ it("List processing", () => { >+ cy.intercept("GET", "/api/v1/preservation/processings*", []); >+ cy.visit("/cgi-bin/koha/preservation/settings"); >+ cy.get("#processing_0").should("not.exist"); >+ cy.intercept("GET", "/api/v1/preservation/processings*", [ >+ get_processing(), >+ ]); >+ cy.visit("/cgi-bin/koha/preservation/settings"); >+ cy.get("#processing_0").should("exist"); >+ }); >+ >+ it("Add processing", () => { >+ cy.intercept("GET", "/api/v1/preservation/processings*", []); >+ cy.visit("/cgi-bin/koha/preservation/settings"); >+ let processing = get_processing(); >+ cy.contains("Add new processing").click(); >+ cy.get("#processing_name").type(processing.name); >+ cy.contains("Add new attribute").click(); >+ let attribute = processing.attributes[0]; >+ cy.get("#attribute_name_0").type(attribute.name); >+ cy.get("#attribute_type_0 .vs__search").type("Authorized{enter}", { >+ force: true, >+ }); >+ cy.get("#attribute_option_0 .vs__search").type( >+ attribute.option_source + "{enter}", >+ { force: true } >+ ); >+ cy.contains("Add new attribute").click(); >+ attribute = processing.attributes[1]; >+ cy.get("#attribute_name_1").type(attribute.name); >+ cy.get("#attribute_type_1 .vs__search").type("Database{enter}", { >+ force: true, >+ }); >+ cy.get("#attribute_option_1 .vs__search").type( >+ attribute.option_source + "{enter}", >+ { force: true } >+ ); >+ cy.contains("Add new attribute").click(); >+ attribute = processing.attributes[2]; >+ cy.get("#attribute_name_2").type(attribute.name); >+ cy.get("#attribute_type_2 .vs__search").type("Free{enter}", { >+ force: true, >+ }); >+ >+ // Submit the form, get 500 >+ cy.intercept("POST", "/api/v1/preservation/processings", { >+ statusCode: 500, >+ error: "Something went wrong", >+ }); >+ cy.get("#processings_add").contains("Submit").click(); >+ cy.get("main div[class='dialog alert']").contains( >+ "Something went wrong: Error: Internal Server Error" >+ ); >+ >+ // Submit the form, success! >+ cy.intercept("POST", "/api/v1/preservation/processings", { >+ statusCode: 201, >+ body: processing, >+ }); >+ cy.intercept("GET", "/api/v1/preservation/processings*", { >+ statusCode: 200, >+ body: [processing], >+ }); >+ cy.get("#processings_add").contains("Submit").click(); >+ cy.get("main div[class='dialog message']").contains( >+ "Processing created" >+ ); >+ cy.get("#processing_0").contains(processing.name); >+ }); >+ >+ it("Edit processing", () => { >+ let processing = get_processing(); >+ cy.intercept("GET", "/api/v1/preservation/processings/*", { >+ statusCode: 200, >+ body: processing, >+ }); >+ cy.intercept("GET", "/api/v1/preservation/processings*", { >+ statusCode: 200, >+ body: [processing], >+ }); >+ cy.visit("/cgi-bin/koha/preservation/settings"); >+ cy.get("#processing_0").contains(processing.name); >+ cy.get("#processing_0").contains("Edit this processing").click(); >+ cy.get("#processing_name").should("have.value", processing.name); >+ let attribute = processing.attributes[0]; >+ cy.get("#attribute_name_0").should("have.value", attribute.name); >+ cy.get("#attribute_type_0 .vs__selected").contains("Authorized value"); >+ cy.get("#attribute_option_0 .vs__selected").contains( >+ attribute.option_source >+ ); >+ attribute = processing.attributes[1]; >+ cy.get("#attribute_name_1").should("have.value", attribute.name); >+ cy.get("#attribute_type_1 .vs__selected").contains("Database column"); >+ cy.get("#attribute_option_1 .vs__selected").contains( >+ attribute.option_source >+ ); >+ attribute = processing.attributes[2]; >+ cy.get("#attribute_name_2").should("have.value", attribute.name); >+ cy.get("#attribute_type_2 .vs__selected").contains("Free text"); >+ cy.get("#attribute_option_2").should("not.exist"); >+ >+ // Submit the form, get 500 >+ cy.intercept("PUT", "/api/v1/preservation/processings/*", { >+ statusCode: 500, >+ error: "Something went wrong", >+ }); >+ cy.get("#processings_add").contains("Submit").click(); >+ cy.get("main div[class='dialog alert']").contains( >+ "Something went wrong: Error: Internal Server Error" >+ ); >+ >+ // Submit the form, success! >+ cy.intercept("PUT", "/api/v1/preservation/processings/*", { >+ statusCode: 200, >+ body: processing, >+ }); >+ cy.get("#processings_add").contains("Submit").click(); >+ cy.get("main div[class='dialog message']").contains( >+ "Processing updated" >+ ); >+ }); >+ >+ it("Delete processing", () => { >+ let processing = get_processing(); >+ cy.intercept("GET", "/api/v1/preservation/processings*", { >+ statusCode: 200, >+ body: [processing], >+ }); >+ >+ // Submit the form, get 500 >+ cy.intercept("DELETE", "/api/v1/preservation/processings/*", { >+ statusCode: 500, >+ error: "Something went wrong", >+ }); >+ cy.visit("/cgi-bin/koha/preservation/settings"); >+ cy.get("#processing_0").contains("Remove this processing").click(); >+ cy.contains("Yes, delete").click(); >+ cy.get("main div[class='dialog alert']").contains( >+ "Something went wrong: Error: Internal Server Error" >+ ); >+ >+ // Submit the form, success! >+ cy.intercept("DELETE", "/api/v1/preservation/processings/*", { >+ statusCode: 204, >+ body: null, >+ }); >+ cy.get("#processing_0").contains("Remove this processing").click(); >+ cy.contains("Yes, delete").click(); >+ cy.get("main div[class='dialog message']").contains( >+ `Processing ${processing.name} deleted` >+ ); >+ }); >+}); >diff --git a/t/cypress/integration/Preservation/Trains.ts b/t/cypress/integration/Preservation/Trains.ts >new file mode 100644 >index 00000000000..94bd7732966 >--- /dev/null >+++ b/t/cypress/integration/Preservation/Trains.ts >@@ -0,0 +1,593 @@ >+import { mount } from "@cypress/vue"; >+ >+function get_attributes() { >+ return [ >+ { >+ processing_attribute_id: 1, >+ processing_id: 1, >+ name: "Country", >+ type: "authorised_value", >+ option_source: "COUNTRY", >+ }, >+ { >+ processing_attribute_id: 2, >+ processing_id: 1, >+ name: "DB", >+ type: "db_column", >+ option_source: "biblio.title", >+ }, >+ { >+ processing_attribute_id: 3, >+ processing_id: 1, >+ name: "Height", >+ type: "free_text", >+ option_source: null, >+ }, >+ ]; >+} >+function get_other_attributes() { >+ return [ >+ { >+ processing_attribute_id: 4, >+ processing_id: 2, >+ name: "Country", >+ type: "authorised_value", >+ option_source: "COUNTRY", >+ }, >+ { >+ processing_attribute_id: 5, >+ processing_id: 2, >+ name: "Width", >+ type: "free_text", >+ option_source: null, >+ }, >+ ]; >+} >+ >+function get_processings() { >+ return [ >+ { >+ name: "new processing", >+ processing_id: 1, >+ attributes: get_attributes(), >+ }, >+ { >+ name: "an other processing", >+ processing_id: 2, >+ attributes: get_other_attributes(), >+ }, >+ ]; >+} >+ >+function get_items() { >+ // This is not a full item but it contains the info we are using >+ return [ >+ { >+ biblio: { >+ biblio_id: 1, >+ title: "a biblio title", >+ }, >+ external_id: "bc_1", >+ item_id: 1, >+ }, >+ { >+ biblio: { >+ biblio_id: 2, >+ title: "an other biblio title", >+ }, >+ external_id: "bc_2", >+ item_id: 2, >+ }, >+ { >+ biblio: { >+ biblio_id: 3, >+ title: "yet an other biblio title", >+ }, >+ external_id: "bc_3", >+ item_id: 3, >+ }, >+ ]; >+} >+ >+function get_train_items() { >+ let train_item_1 = get_items()[0]; >+ let processing_attributes = get_attributes(); >+ train_item_1.attributes = [ >+ { >+ processing_attribute: processing_attributes[0], >+ processing_attribute_id: >+ processing_attributes[0].processing_attribute_id, >+ value: "Argentina", >+ }, >+ { >+ processing_attribute: processing_attributes[1], >+ processing_attribute_id: >+ processing_attributes[1].processing_attribute_id, >+ value: "a biblio title modified", >+ }, >+ { >+ processing_attribute: processing_attributes[2], >+ processing_attribute_id: >+ processing_attributes[2].processing_attribute_id, >+ value: "12cm", >+ }, >+ ]; >+ train_item_1.added_on = "2023-03-31T12:23:34+00:00"; >+ train_item_1.processing_id = 1; >+ train_item_1.item_id = 1; >+ >+ let train_item_2 = get_items()[1]; >+ let processing_attributes = get_attributes(); >+ train_item_2.attributes = [ >+ { >+ processing_attribute: processing_attributes[0], >+ processing_attribute_id: >+ processing_attributes[0].processing_attribute_id, >+ value: "Uruguay", >+ }, >+ { >+ processing_attribute: processing_attributes[1], >+ processing_attribute_id: >+ processing_attributes[1].processing_attribute_id, >+ value: "an other modified title", >+ }, >+ { >+ processing_attribute: processing_attributes[2], >+ processing_attribute_id: >+ processing_attributes[2].processing_attribute_id, >+ value: "34cm", >+ }, >+ ]; >+ train_item_2.added_on = "2023-04-01T12:34:56+00:00"; >+ train_item_2.processing_id = 1; >+ train_item_2.item_id = 2; >+ >+ let train_item_3 = get_items()[0]; >+ let processing_attributes = get_other_attributes(); >+ train_item_3.attributes = [ >+ { >+ processing_attribute: processing_attributes[0], >+ processing_attribute_id: >+ processing_attributes[0].processing_attribute_id, >+ value: "Bolivia", >+ }, >+ { >+ processing_attribute: processing_attributes[1], >+ processing_attribute_id: >+ processing_attributes[1].processing_attribute_id, >+ value: "W 123cm", >+ }, >+ ]; >+ train_item_3.added_on = "2023-04-02T12:34:56+00:00"; >+ train_item_3.processing_id = 2; >+ train_item_3.item_id = 3; >+ >+ return [train_item_1, train_item_2, train_item_3]; >+} >+ >+function get_train() { >+ let processings = get_processings(); >+ return { >+ train_id: 1, >+ name: "My train", >+ description: "Just a train", >+ default_processing_id: processings[0].processing_id, >+ not_for_loan: "42", >+ created_on: "2023-04-05T10:16:27+00:00", >+ closed_on: null, >+ sent_on: null, >+ received_on: null, >+ items: [], >+ default_processing: processings[0], >+ }; >+} >+ >+describe("Trains", () => { >+ beforeEach(() => { >+ cy.login(); >+ cy.title().should("eq", "Koha staff interface"); >+ cy.intercept( >+ "GET", >+ "/cgi-bin/koha/svc/config/systempreferences/?pref=PreservationModule", >+ '{"value":"1"}' >+ ); >+ cy.intercept( >+ "GET", >+ "/cgi-bin/koha/svc/config/systempreferences/?pref=PreservationNotForLoanWaitingListIn", >+ '{"value":"24"}' >+ ); >+ cy.intercept( >+ "GET", >+ "/cgi-bin/koha/svc/config/systempreferences/?pref=PreservationNotForLoanDefaultTrainIn", >+ '{"value":"42"}' >+ ); >+ cy.intercept( >+ "GET", >+ "/api/v1/authorised_value_categories/NOT_LOAN/authorised_values", >+ [ >+ { >+ category_name: "NOT_LOAN", >+ description: "Ordered", >+ value: "-1", >+ }, >+ { >+ category_name: "NOT_LOAN", >+ description: "Not for loan", >+ value: "1", >+ }, >+ { >+ category_name: "NOT_LOAN", >+ description: "Staff collection", >+ value: "2", >+ }, >+ { >+ category_name: "NOT_LOAN", >+ description: "Added to bundle", >+ value: "3", >+ }, >+ { >+ category_name: "NOT_LOAN", >+ description: "In preservation", >+ value: "24", >+ }, >+ { >+ category_name: "NOT_LOAN", >+ description: "In preservation external", >+ value: "42", >+ }, >+ { >+ category_name: "NOT_LOAN", >+ description: "In preservation other", >+ value: "43", >+ }, >+ ] >+ ); >+ }); >+ >+ it("List trains", () => { >+ // GET trains returns 500 >+ cy.intercept("GET", "/api/v1/preservation/trains*", { >+ statusCode: 500, >+ error: "Something went wrong", >+ }); >+ cy.visit("/cgi-bin/koha/preservation/home.pl"); >+ cy.get("#navmenulist").contains("Trains").click(); >+ cy.get("main div[class='dialog alert']").contains( >+ /Something went wrong/ >+ ); >+ >+ // GET trains returns empty list >+ cy.intercept("GET", "/api/v1/*", []); >+ cy.visit("/cgi-bin/koha/preservation/trains"); >+ cy.get("#trains_list").contains("There are no trains defined"); >+ >+ // GET trains returns something >+ let train = get_train(); >+ let trains = [train]; >+ >+ cy.intercept("GET", "/api/v1/preservation/trains*", { >+ statusCode: 200, >+ body: trains, >+ headers: { >+ "X-Base-Total-Count": "1", >+ "X-Total-Count": "1", >+ }, >+ }); >+ cy.intercept("GET", "/api/v1/preservation/trains/*", train); >+ cy.visit("/cgi-bin/koha/preservation/trains"); >+ cy.get("#trains_list").contains("Showing 1 to 1 of 1 entries"); >+ }); >+ >+ it("Add train", () => { >+ cy.intercept("GET", "/api/v1/preservation/trains", []); >+ cy.intercept( >+ "GET", >+ "/api/v1/preservation/processings", >+ get_processings() >+ ); >+ cy.visit("/cgi-bin/koha/preservation/trains"); >+ let train = get_train(); >+ cy.contains("New train").click(); >+ cy.get("#train_name").type(train.name); >+ cy.get("#train_description").type(train.description); >+ // Confirm that the default not_for_loan is selected >+ cy.get("#not_for_loan .vs__selected").contains( >+ "In preservation external" >+ ); >+ // Change it >+ cy.get("#not_for_loan .vs__search").type( >+ "In preservation other{enter}" >+ ); >+ cy.get("#train_default_processing .vs__search").type( >+ "new processing{enter}" >+ ); >+ >+ // Submit the form, get 500 >+ cy.intercept("POST", "/api/v1/preservation/trains", { >+ statusCode: 500, >+ error: "Something went wrong", >+ }); >+ cy.get("#trains_add").contains("Submit").click(); >+ cy.get("main div[class='dialog alert']").contains( >+ "Something went wrong: Error: Internal Server Error" >+ ); >+ >+ // Submit the form, success! >+ cy.intercept("POST", "/api/v1/preservation/trains", { >+ statusCode: 201, >+ body: train, >+ }); >+ cy.get("#trains_add").contains("Submit").click(); >+ cy.get("main div[class='dialog message']").contains("Train created"); >+ }); >+ >+ it("Edit train", () => { >+ let train = get_train(); >+ let processings = get_processings(); >+ cy.intercept("GET", "/api/v1/preservation/trains/*", train); >+ cy.intercept("GET", "/api/v1/preservation/trains", [train]); >+ cy.intercept("GET", "/api/v1/preservation/processings*", processings); >+ cy.visit("/cgi-bin/koha/preservation/trains"); >+ cy.get("#trains_list table tbody tr:first").contains("Edit").click(); >+ cy.get("#train_name").should("have.value", train.name); >+ cy.get("#train_description").should("have.value", train.description); >+ cy.get("#not_for_loan .vs__selected").contains( >+ "In preservation external" >+ ); >+ cy.get("#train_default_processing .vs__selected").contains( >+ train.default_processing.name >+ ); >+ >+ // Submit the form, get 500 >+ cy.intercept("PUT", "/api/v1/preservation/trains/*", { >+ statusCode: 500, >+ error: "Something went wrong", >+ }); >+ cy.get("#trains_add").contains("Submit").click(); >+ cy.get("main div[class='dialog alert']").contains( >+ "Something went wrong: Error: Internal Server Error" >+ ); >+ >+ // Submit the form, success! >+ cy.intercept("PUT", "/api/v1/preservation/trains/*", { >+ statusCode: 200, >+ body: train, >+ }); >+ cy.intercept("GET", "/api/v1/preservation/trains", { >+ statusCode: 200, >+ body: [train], >+ }); >+ cy.get("#trains_add").contains("Submit").click(); >+ cy.get("main div[class='dialog message']").contains("Train updated"); >+ }); >+ >+ it("Simple show train", () => { >+ let train = get_train(); >+ let trains = [train]; >+ >+ cy.intercept("GET", "/api/v1/preservation/trains*", { >+ statusCode: 200, >+ body: trains, >+ headers: { >+ "X-Base-Total-Count": "1", >+ "X-Total-Count": "1", >+ }, >+ }); >+ cy.intercept("GET", "/api/v1/preservation/trains/*", train); >+ cy.visit("/cgi-bin/koha/preservation/trains"); >+ let name_link = cy.get("#trains_list table tbody tr:first td:first a"); >+ name_link.should( >+ "have.text", >+ train.name + " (#" + train.train_id + ")" >+ ); >+ name_link.click(); >+ cy.get("#trains_show h2").contains("Train #" + train.train_id); >+ >+ cy.contains("Name:" + train.name); >+ cy.contains("Description:" + train.description); >+ cy.contains( >+ "Status for item added to this train:" + "In preservation external" >+ ); >+ cy.contains("Default processing:" + train.default_processing.name); >+ }); >+ >+ it("Show train close, send, receive", () => { >+ let train = get_train(); >+ cy.intercept("GET", "/api/v1/preservation/trains/" + train.train_id, { >+ statusCode: 200, >+ body: train, >+ }).as("get-train"); >+ cy.visit("/cgi-bin/koha/preservation/trains/" + train.train_id); >+ cy.wait("@get-train"); >+ cy.contains("Closed on:").should("not.exist"); >+ cy.contains("Sent on:").should("not.exist"); >+ cy.contains("Received on:").should("not.exist"); >+ >+ let closed_train = Object.assign({}, train); >+ closed_train.closed_on = "2022-10-27 12:34:56"; >+ cy.intercept("PUT", "/api/v1/preservation/trains/" + train.train_id, { >+ statusCode: 201, >+ body: closed_train, >+ }).as("set-train"); >+ cy.intercept("GET", "/api/v1/preservation/trains/" + train.train_id, { >+ statusCode: 200, >+ body: closed_train, >+ }).as("get-train"); >+ cy.get("#toolbar").contains("Close").click(); >+ cy.wait("@get-train"); >+ cy.contains("Closed on:").should("exist"); >+ cy.contains("Sent on:").should("not.exist"); >+ cy.contains("Received on:").should("not.exist"); >+ >+ let sent_train = Object.assign({}, closed_train); >+ sent_train.sent_on = "2022-10-28 12:34:56"; >+ cy.intercept("PUT", "/api/v1/preservation/trains/" + train.train_id, { >+ statusCode: 201, >+ body: sent_train, >+ }).as("set-train"); >+ cy.intercept("GET", "/api/v1/preservation/trains/" + train.train_id, { >+ statusCode: 200, >+ body: sent_train, >+ }).as("get-train"); >+ cy.get("#toolbar").contains("Send").click(); >+ cy.wait("@get-train"); >+ cy.contains("Closed on:").should("exist"); >+ cy.contains("Sent on:").should("exist"); >+ cy.contains("Received on:").should("not.exist"); >+ >+ let received_train = Object.assign({}, sent_train); >+ received_train.received_on = "2022-10-29 12:34:56"; >+ cy.intercept("PUT", "/api/v1/preservation/trains/" + train.train_id, { >+ statusCode: 201, >+ body: received_train, >+ }).as("set-train"); >+ cy.intercept("GET", "/api/v1/preservation/trains/" + train.train_id, { >+ statusCode: 200, >+ body: received_train, >+ }).as("get-train"); >+ cy.get("#toolbar").contains("Receive").click(); >+ cy.wait("@get-train"); >+ cy.contains("Closed on:").should("exist"); >+ cy.contains("Sent on:").should("exist"); >+ cy.contains("Received on:").should("exist"); >+ }); >+ >+ it("Delete train", () => { >+ let train = get_train(); >+ cy.intercept("GET", "/api/v1/preservation/trains*", { >+ statusCode: 200, >+ body: [train], >+ headers: { >+ "X-Base-Total-Count": "1", >+ "X-Total-Count": "1", >+ }, >+ }); >+ cy.visit("/cgi-bin/koha/preservation/trains"); >+ >+ // Submit the form, get 500 >+ cy.intercept( >+ "DELETE", >+ "/api/v1/preservation/trains/" + train.train_id, >+ { >+ statusCode: 500, >+ error: "Something went wrong", >+ } >+ ); >+ cy.get("#trains_list table tbody tr:first").contains("Delete").click(); >+ cy.contains("Yes, delete").click(); >+ cy.get("main div[class='dialog alert']").contains( >+ "Something went wrong: Error: Internal Server Error" >+ ); >+ >+ // Submit the form, success! >+ cy.intercept( >+ "DELETE", >+ "/api/v1/preservation/trains/" + train.train_id, >+ { >+ statusCode: 201, >+ body: null, >+ } >+ ); >+ cy.get("#trains_list table tbody tr:first").contains("Delete").click(); >+ cy.contains("Yes, delete").click(); >+ cy.get("main div[class='dialog message']").contains( >+ `Train ${train.name} deleted` >+ ); >+ }); >+ >+ it("Add new item to a train", () => { >+ let train = get_train(); >+ cy.intercept( >+ "GET", >+ "/api/v1/preservation/trains/" + train.train_id, >+ train >+ ); >+ let processings = get_processings(); >+ cy.intercept("GET", "/api/v1/preservation/processings*", processings); >+ cy.intercept( >+ "GET", >+ "/api/v1/preservation/processings/" + processings[0].processing_id, >+ processings[0] >+ ); >+ cy.visit("/cgi-bin/koha/preservation/trains/" + train.train_id); >+ cy.contains("Add items").click(); >+ cy.get("#barcode").type("bc_1"); >+ cy.intercept("GET", "/api/v1/preservation/waiting-list/items*", []); >+ cy.contains("Submit").click(); >+ cy.get("div[class='dialog alert modal']").contains( >+ "Cannot find item with this barcode. It must be in the waiting list." >+ ); >+ cy.get("#close_modal").click(); >+ >+ let item = get_items()[0]; >+ cy.intercept("GET", "/api/v1/preservation/waiting-list/items*", [item]); >+ cy.contains("Submit").click(); >+ cy.intercept( >+ "POST", >+ `/api/v1/preservation/trains/${train.train_id}/items`, >+ { >+ statusCode: 201, >+ body: item, // Not correct but not important >+ } >+ ); >+ cy.contains("Itemnumber:" + item.item_id); >+ cy.get("#processing .vs__selected").contains( >+ train.default_processing.name >+ ); >+ cy.contains("Country:"); >+ cy.get("#attribute_0 .vs__search").type("Argentin{enter}"); >+ cy.contains("DB:"); >+ cy.get("#attribute_1").should("have.value", item.biblio.title); >+ cy.get("#attribute_1").type(" modified"); >+ cy.contains("Height:"); >+ cy.get("#attribute_2").type("42cm"); >+ >+ let train_items = get_train_items(); >+ let train_with_one_item = Object.assign({}, train); >+ train_with_one_item.items = [train_items[0]]; >+ >+ cy.intercept( >+ "GET", >+ "/api/v1/preservation/trains/" + train.train_id, >+ train_with_one_item >+ ); >+ cy.contains("Submit").click(); >+ cy.get("#trains_show").contains("Showing 1 to 1 of 1 entries"); >+ >+ let train_with_2_items = Object.assign({}, train); >+ train_with_2_items.items = [train_items[0], train_items[1]]; >+ cy.intercept( >+ "GET", >+ "/api/v1/preservation/trains/" + train.train_id, >+ train_with_2_items >+ ); >+ cy.visit("/cgi-bin/koha/preservation/trains/" + train.train_id); >+ cy.get("#trains_show table").should("exist"); >+ cy.get("#trains_show").contains("Showing 1 to 2 of 2 entries"); >+ train_with_2_items.items.forEach(train_item => { >+ train_item.attributes.forEach(attribute => { >+ cy.get("td").contains(attribute.value); >+ }); >+ }); >+ >+ let train_with_3_items = Object.assign({}, train); >+ train_with_3_items.items = [ >+ train_items[0], >+ train_items[1], >+ train_items[2], >+ ]; >+ cy.intercept( >+ "GET", >+ "/api/v1/preservation/trains/" + train.train_id, >+ train_with_3_items >+ ); >+ cy.visit("/cgi-bin/koha/preservation/trains/" + train.train_id); >+ cy.get("#trains_show table").should("not.exist"); >+ train_with_3_items.items.forEach((train_item, i) => { >+ train_item.attributes.forEach(attribute => { >+ let re = new RegExp(attribute.value); >+ cy.get(`#item_${i}`).contains(re); >+ }); >+ }); >+ }); >+}); >diff --git a/t/cypress/integration/Preservation/WaitingList.ts b/t/cypress/integration/Preservation/WaitingList.ts >new file mode 100644 >index 00000000000..4e4e1c2b5d1 >--- /dev/null >+++ b/t/cypress/integration/Preservation/WaitingList.ts >@@ -0,0 +1,185 @@ >+import { mount } from "@cypress/vue"; >+ >+function get_items() { >+ // This is not a full item but it contains the info we are using >+ return [ >+ { >+ biblio: { >+ biblio_id: 1, >+ title: "a biblio title", >+ }, >+ external_id: "bc_1", >+ item_id: 1, >+ }, >+ { >+ biblio: { >+ biblio_id: 2, >+ title: "yet another biblio title", >+ }, >+ external_id: "bc_3", >+ item_id: 3, >+ }, >+ ]; >+} >+describe("WaitingList", () => { >+ beforeEach(() => { >+ cy.login(); >+ cy.title().should("eq", "Koha staff interface"); >+ cy.intercept( >+ "GET", >+ "/cgi-bin/koha/svc/config/systempreferences/?pref=PreservationModule", >+ '{"value":"1"}' >+ ); >+ cy.intercept( >+ "GET", >+ "/cgi-bin/koha/svc/config/systempreferences/?pref=PreservationNotForLoanWaitingListIn", >+ '{"value":"24"}' >+ ); >+ cy.intercept( >+ "GET", >+ "/cgi-bin/koha/svc/config/systempreferences/?pref=PreservationNotForLoanDefaultTrainIn", >+ '{"value":"42"}' >+ ); >+ }); >+ >+ it("List", () => { >+ cy.intercept( >+ "GET", >+ "/cgi-bin/koha/svc/config/systempreferences/?pref=PreservationNotForLoanWaitingListIn", >+ '{"value":""}' >+ ); >+ cy.visit("/cgi-bin/koha/preservation/home.pl"); >+ cy.intercept("GET", "/api/v1/preservation/waiting-list/items*", []); >+ cy.get("#navmenulist").contains("Waiting list").click(); >+ cy.get("#waiting-list").contains( >+ "You need to configure this module first." >+ ); >+ >+ cy.intercept( >+ "GET", >+ "/cgi-bin/koha/svc/config/systempreferences/?pref=PreservationNotForLoanWaitingListIn", >+ '{"value":"42"}' >+ ); >+ cy.visit("/cgi-bin/koha/preservation/home.pl"); >+ cy.intercept("GET", "/api/v1/preservation/waiting-list/items*", []); >+ cy.get("#navmenulist").contains("Waiting list").click(); >+ cy.get("#waiting-list").contains( >+ "There are no items in the waiting list" >+ ); >+ }); >+ >+ it("Add to waiting list", () => { >+ cy.intercept("GET", "/api/v1/preservation/waiting-list/items*", []); >+ cy.visit("/cgi-bin/koha/preservation/waiting-list"); >+ cy.intercept("POST", "/api/v1/preservation/waiting-list/items", { >+ statusCode: 500, >+ error: "Something went wrong", >+ }); >+ cy.get("#waiting-list").contains("Add to waiting list").click(); >+ cy.get("#barcode_list").type("bc_1\nbc_2\nbc_3"); >+ cy.contains("Submit").click(); >+ cy.get("main div[class='dialog alert']").contains( >+ "Something went wrong: Error: Internal Server Error" >+ ); >+ >+ cy.intercept("GET", "/api/v1/preservation/waiting-list/items*", { >+ statusCode: 200, >+ body: get_items(), >+ headers: { >+ "X-Base-Total-Count": "2", >+ "X-Total-Count": "2", >+ }, >+ }).as("get-items"); >+ cy.intercept("POST", "/api/v1/preservation/waiting-list/items", [ >+ { item_id: 1 }, >+ { item_id: 3 }, >+ ]); >+ cy.get("#waiting-list").contains("Add to waiting list").click(); >+ cy.get("#barcode_list").type("bc_1\nbc_2\nbc_3"); >+ cy.contains("Submit").click(); >+ cy.wait("@get-items"); >+ cy.get("main div[class='dialog message']").contains( >+ "2 new items added." >+ ); >+ }); >+ >+ it("Add to waiting list then add to a train", () => { >+ let train = { >+ description: "yet another train", >+ name: "a train", >+ train_id: 1, >+ }; >+ cy.intercept("GET", "/api/v1/preservation/trains*", [train]); >+ cy.visit("/cgi-bin/koha/preservation/waiting-list"); >+ >+ cy.intercept("GET", "/api/v1/preservation/waiting-list/items*", { >+ statusCode: 200, >+ body: get_items(), >+ headers: { >+ "X-Base-Total-Count": "2", >+ "X-Total-Count": "2", >+ }, >+ }).as("get-items"); >+ cy.intercept("POST", "/api/v1/preservation/waiting-list/items", [ >+ { item_id: 1 }, >+ { item_id: 3 }, >+ ]); >+ cy.get("#waiting-list").contains("Add to waiting list").click(); >+ cy.get("#barcode_list").type("bc_1\nbc_2\nbc_3"); >+ cy.contains("Submit").click(); >+ cy.wait("@get-items"); >+ cy.get("main div[class='dialog message']").contains( >+ "2 new items added." >+ ); >+ cy.contains("Add last 2 items to a train").click(); >+ cy.get("#train_id .vs__search").type(train.name + "{enter}"); >+ cy.intercept( >+ "POST", >+ "/api/v1/preservation/trains/" + train.train_id + "/items/batch", >+ req => { >+ req.reply({ >+ statusCode: 201, >+ body: req.body, >+ }); >+ } >+ ); >+ cy.contains("Submit").click(); >+ cy.get("main div[class='dialog message']").contains( >+ `2 items have been added to train ${train.train_id}.` >+ ); >+ }); >+ >+ it("Remove item from waiting list", () => { >+ cy.intercept("GET", "/api/v1/preservation/waiting-list/items*", { >+ statusCode: 200, >+ body: get_items(), >+ headers: { >+ "X-Base-Total-Count": "2", >+ "X-Total-Count": "2", >+ }, >+ }); //.as("get-items") >+ cy.visit("/cgi-bin/koha/preservation/waiting-list"); >+ >+ // Submit the form, get 500 >+ cy.intercept("DELETE", "/api/v1/preservation/waiting-list/items/*", { >+ statusCode: 500, >+ error: "Something went wrong", >+ }); >+ cy.get("#waiting-list table tbody tr:first").contains("Remove").click(); >+ cy.contains("Yes, remove").click(); >+ cy.get("main div[class='dialog alert']").contains( >+ "Something went wrong: Error: Internal Server Error" >+ ); >+ >+ // Submit the form, success! >+ cy.intercept("DELETE", "/api/v1/preservation/waiting-list/items/*", { >+ statusCode: 204, >+ body: null, >+ }); >+ cy.get("#waiting-list table tbody tr:first").contains("Remove").click(); >+ cy.contains("Yes, remove").click(); >+ cy.get("main div[class='dialog message']").contains( >+ `Item removed from the waiting list` >+ ); >+ }); >+}); >diff --git a/t/db_dependent/Koha/Preservation/Processings.t b/t/db_dependent/Koha/Preservation/Processings.t >new file mode 100755 >index 00000000000..d0c1bb1d399 >--- /dev/null >+++ b/t/db_dependent/Koha/Preservation/Processings.t >@@ -0,0 +1,58 @@ >+#!/usr/bin/perl >+ >+# This file is part of Koha >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Test::More tests => 1; >+use Test::Exception; >+use Test::Warn; >+ >+use Koha::Preservation::Processings; >+use Koha::Database; >+ >+use t::lib::TestBuilder; >+use t::lib::Mocks; >+ >+my $schema = Koha::Database->new->schema; >+my $builder = t::lib::TestBuilder->new; >+ >+subtest 'attributes' => sub { >+ >+ plan tests => 4; >+ >+ $schema->storage->txn_begin; >+ >+ my $processing = $builder->build_object( { class => 'Koha::Preservation::Processings' } ); >+ >+ my $attributes = [ >+ { name => 'color', type => 'authorised_value', option_source => 'COLORS' }, >+ { name => 'title', type => 'db_column', option_source => '245$a' }, >+ { >+ name => 'height', >+ type => 'free_text' >+ }, >+ ]; >+ $processing->attributes($attributes); >+ my $fetched_attributes = $processing->attributes; >+ is( ref($fetched_attributes), 'Koha::Preservation::Processing::Attributes' ); >+ is( $fetched_attributes->count, 3 ); >+ $processing->attributes( [] ); >+ is( ref($fetched_attributes), 'Koha::Preservation::Processing::Attributes' ); >+ is( $fetched_attributes->count, 0 ); >+ >+ $schema->storage->txn_rollback; >+}; >diff --git a/t/db_dependent/Koha/Preservation/Trains.t b/t/db_dependent/Koha/Preservation/Trains.t >new file mode 100755 >index 00000000000..9fd1ea2c3ce >--- /dev/null >+++ b/t/db_dependent/Koha/Preservation/Trains.t >@@ -0,0 +1,102 @@ >+#!/usr/bin/perl >+ >+# This file is part of Koha >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Test::More tests => 2; >+use Test::Exception; >+use Test::Warn; >+ >+use Koha::Preservation::Trains; >+use Koha::Database; >+ >+use t::lib::TestBuilder; >+use t::lib::Mocks; >+ >+my $schema = Koha::Database->new->schema; >+my $builder = t::lib::TestBuilder->new; >+ >+subtest 'default_processing' => sub { >+ >+ plan tests => 3; >+ >+ $schema->storage->txn_begin; >+ >+ my $processing = $builder->build_object( { class => 'Koha::Preservation::Processings' } ); >+ my $another_processing = $builder->build_object( { class => 'Koha::Preservation::Processings' } ); >+ >+ my $train = $builder->build_object( { class => 'Koha::Preservation::Trains', value => { default_processing_id => $processing->processing_id } } ); >+ >+ my $default_processing = $train->default_processing; >+ is( ref($default_processing), 'Koha::Preservation::Processing', '->default_processing returns a Koha::Preservation::Processing object' ); >+ is( $default_processing->processing_id, $processing->processing_id, 'correct processing is returned' ); >+ $processing->delete; >+ $default_processing = $train->get_from_storage->default_processing; >+ is( $default_processing, undef, 'deleting the processing does not delete the train' ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'add_items & items' => sub { >+ plan tests => 9; >+ >+ $schema->storage->txn_begin; >+ >+ my $not_for_loan_waiting_list_in = 24; >+ my $not_for_loan_train_in = 42; >+ my $train = $builder->build_object( >+ { >+ class => 'Koha::Preservation::Trains', >+ value => { >+ not_for_loan => $not_for_loan_train_in, >+ closed_on => undef, >+ sent_on => undef, >+ received_on => undef >+ } >+ } >+ ); >+ my $item_1 = $builder->build_sample_item; >+ my $item_2 = $builder->build_sample_item; >+ my $item_3 = $builder->build_sample_item; >+ >+ $builder->build_object( { class => 'Koha::AuthorisedValues', value => { category => 'NOT_LOAN', authorised_value => $not_for_loan_waiting_list_in } } ); >+ $item_1->notforloan($not_for_loan_waiting_list_in)->store; >+ $item_2->notforloan(0)->store; # item_2 is not in the waiting list >+ $item_3->notforloan($not_for_loan_waiting_list_in)->store; >+ t::lib::Mocks::mock_preference( 'PreservationNotForLoanWaitingListIn', $not_for_loan_waiting_list_in ); >+ warning_is { >+ $train->add_items( [ { item_id => $item_1->itemnumber }, { item_id => $item_2->itemnumber }, { barcode => $item_3->barcode } ] ); >+ } >+ 'Item not added to train: [Cannot add item to train, it is not in the waiting list]'; >+ my $items_train = $train->items; >+ is( $items_train->count, 2, '2 items added to the train' ); >+ my $item_train_1 = $items_train->find( { item_id => $item_1->itemnumber } ); >+ my $item_train_3 = $items_train->find( { item_id => $item_3->itemnumber } ); >+ my $catalogue_item_1 = $item_train_1->catalogue_item; >+ is( ref($catalogue_item_1), 'Koha::Item' ); >+ is( $catalogue_item_1->notforloan, $not_for_loan_train_in ); >+ >+ my $catalogue_item_3 = $item_train_3->catalogue_item; >+ is( ref($catalogue_item_3), 'Koha::Item' ); >+ is( $catalogue_item_3->notforloan, $not_for_loan_train_in ); >+ >+ is( $item_1->get_from_storage->notforloan, $not_for_loan_train_in ); >+ is( $item_2->get_from_storage->notforloan, 0 ); >+ is( $item_3->get_from_storage->notforloan, $not_for_loan_train_in ); >+ >+ $schema->storage->txn_rollback; >+}; >diff --git a/t/db_dependent/api/v1/preservation_Processings.t b/t/db_dependent/api/v1/preservation_Processings.t >new file mode 100755 >index 00000000000..f0d86aec10d >--- /dev/null >+++ b/t/db_dependent/api/v1/preservation_Processings.t >@@ -0,0 +1,369 @@ >+#!/usr/bin/env perl >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Test::More tests => 5; >+use Test::Mojo; >+ >+use t::lib::TestBuilder; >+use t::lib::Mocks; >+ >+use Koha::Preservation::Processings; >+use Koha::Database; >+ >+my $schema = Koha::Database->new->schema; >+my $builder = t::lib::TestBuilder->new; >+ >+my $t = Test::Mojo->new('Koha::REST::V1'); >+t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); >+ >+subtest 'list() tests' => sub { >+ >+ plan tests => 8; >+ >+ $schema->storage->txn_begin; >+ >+ Koha::Preservation::Processings->search->delete; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**30 } >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ ## Authorized user tests >+ # No processings, so empty array should be returned >+ $t->get_ok("//$userid:$password@/api/v1/preservation/processings")->status_is(200) >+ ->json_is( [] ); >+ >+ my $processing = $builder->build_object( >+ { >+ class => 'Koha::Preservation::Processings', >+ } >+ ); >+ >+ # One processing created, should get returned >+ $t->get_ok("//$userid:$password@/api/v1/preservation/processings")->status_is(200) >+ ->json_is( [ $processing->to_api ] ); >+ >+ # Unauthorized access >+ $t->get_ok("//$unauth_userid:$password@/api/v1/preservation/processings") >+ ->status_is(403); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'get() tests' => sub { >+ >+ plan tests => 11; >+ >+ $schema->storage->txn_begin; >+ >+ my $processing = >+ $builder->build_object( { class => 'Koha::Preservation::Processings' } ); >+ my $attributes = [ >+ { >+ name => 'color', >+ type => 'authorised_value', >+ option_source => 'COLORS' >+ }, >+ { name => 'title', type => 'db_column', option_source => '245$a' }, >+ { >+ name => 'height', >+ type => 'free_text' >+ }, >+ ]; >+ $attributes = $processing->attributes($attributes); >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**30 } >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ # This processing exists, should get returned >+ $t->get_ok( "//$userid:$password@/api/v1/preservation/processings/" >+ . $processing->processing_id )->status_is(200) >+ ->json_is( $processing->to_api ); >+ >+ # Return one processing with attributes >+ $t->get_ok( "//$userid:$password@/api/v1/preservation/processings/" >+ . $processing->processing_id => {'x-koha-embed' => 'attributes'} )->status_is(200) >+ ->json_is( { %{ $processing->to_api }, attributes => $attributes->to_api }); >+ >+ # Unauthorized access >+ $t->get_ok( "//$unauth_userid:$password@/api/v1/preservation/processings/" >+ . $processing->processing_id )->status_is(403); >+ >+ # Attempt to get non-existent processing >+ my $processing_to_delete = >+ $builder->build_object( { class => 'Koha::Preservation::Processings' } ); >+ my $non_existent_id = $processing_to_delete->processing_id; >+ $processing_to_delete->delete; >+ >+ $t->get_ok("//$userid:$password@/api/v1/preservation/processings/$non_existent_id") >+ ->status_is(404)->json_is( '/error' => 'Processing not found' ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'add() tests' => sub { >+ >+ plan tests => 15; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**30 } >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ my $default_processing = $builder->build_object({class => 'Koha::Preservation::Processings'}); >+ my $processing = { >+ name => "processing name", >+ }; >+ >+ # Unauthorized attempt to write >+ $t->post_ok( "//$unauth_userid:$password@/api/v1/preservation/processings" => json => >+ $processing )->status_is(403); >+ >+ # Authorized attempt to write invalid data >+ my $processing_with_invalid_field = { >+ blah => "processing Blah", >+ %$processing, >+ }; >+ >+ $t->post_ok( "//$userid:$password@/api/v1/preservation/processings" => json => >+ $processing_with_invalid_field )->status_is(400)->json_is( >+ "/errors" => [ >+ { >+ message => "Properties not allowed: blah.", >+ path => "/body" >+ } >+ ] >+ ); >+ >+ # Authorized attempt to write >+ my $processing_id = >+ $t->post_ok( >+ "//$userid:$password@/api/v1/preservation/processings" => json => $processing ) >+ ->status_is( 201, 'SWAGGER3.2.1' )->header_like( >+ Location => qr|^/api/v1/preservation/processings/\d*|, >+ 'SWAGGER3.4.1' >+ )->json_is( '/name' => $processing->{name} ) >+ ->tx->res->json->{processing_id}; >+ >+ # Authorized attempt to create with null id >+ $processing->{processing_id} = undef; >+ $t->post_ok( >+ "//$userid:$password@/api/v1/preservation/processings" => json => $processing ) >+ ->status_is(400)->json_has('/errors'); >+ >+ # Authorized attempt to create with existing id >+ $processing->{processing_id} = $processing_id; >+ $t->post_ok( >+ "//$userid:$password@/api/v1/preservation/processings" => json => $processing ) >+ ->status_is(400)->json_is( >+ "/errors" => [ >+ { >+ message => "Read-only.", >+ path => "/body/processing_id" >+ } >+ ] >+ ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'update() tests' => sub { >+ >+ plan tests => 15; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**30 } >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ my $processing_id = >+ $builder->build_object( { class => 'Koha::Preservation::Processings' } )->processing_id; >+ >+ # Unauthorized attempt to update >+ $t->put_ok( >+ "//$unauth_userid:$password@/api/v1/preservation/processings/$processing_id" => >+ json => { name => 'New unauthorized name change' } )->status_is(403); >+ >+ # Attempt partial update on a PUT >+ my $processing_with_missing_field = { >+ }; >+ >+ $t->put_ok( >+ "//$userid:$password@/api/v1/preservation/processings/$processing_id" => json => >+ $processing_with_missing_field )->status_is(400) >+ ->json_is( "/errors" => >+ [ { message => "Missing property.", path => "/body/name" } ] ); >+ >+ my $default_processing = $builder->build_object({class => 'Koha::Preservation::Processings'}); >+ # Full object update on PUT >+ my $processing_with_updated_field = { >+ name => "New name", >+ }; >+ >+ $t->put_ok( >+ "//$userid:$password@/api/v1/preservation/processings/$processing_id" => json => >+ $processing_with_updated_field )->status_is(200) >+ ->json_is( '/name' => 'New name' ); >+ >+ # Authorized attempt to write invalid data >+ my $processing_with_invalid_field = { >+ blah => "processing Blah", >+ %$processing_with_updated_field, >+ }; >+ >+ $t->put_ok( >+ "//$userid:$password@/api/v1/preservation/processings/$processing_id" => json => >+ $processing_with_invalid_field )->status_is(400)->json_is( >+ "/errors" => [ >+ { >+ message => "Properties not allowed: blah.", >+ path => "/body" >+ } >+ ] >+ ); >+ >+ # Attempt to update non-existent processing >+ my $processing_to_delete = >+ $builder->build_object( { class => 'Koha::Preservation::Processings' } ); >+ my $non_existent_id = $processing_to_delete->processing_id; >+ $processing_to_delete->delete; >+ >+ $t->put_ok( "//$userid:$password@/api/v1/preservation/processings/$non_existent_id" => >+ json => $processing_with_updated_field )->status_is(404); >+ >+ # Wrong method (POST) >+ $processing_with_updated_field->{processing_id} = 2; >+ >+ $t->post_ok( >+ "//$userid:$password@/api/v1/preservation/processings/$processing_id" => json => >+ $processing_with_updated_field )->status_is(404); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'delete() tests' => sub { >+ >+ plan tests => 7; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**30 } >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ my $processing_id = >+ $builder->build_object( { class => 'Koha::Preservation::Processings' } )->processing_id; >+ >+ # Unauthorized attempt to delete >+ $t->delete_ok( >+ "//$unauth_userid:$password@/api/v1/preservation/processings/$processing_id") >+ ->status_is(403); >+ >+ # Delete existing processing >+ $t->delete_ok("//$userid:$password@/api/v1/preservation/processings/$processing_id") >+ ->status_is( 204, 'SWAGGER3.2.4' )->content_is( '', 'SWAGGER3.3.4' ); >+ >+ # Attempt to delete non-existent processing >+ $t->delete_ok("//$userid:$password@/api/v1/preservation/processings/$processing_id") >+ ->status_is(404); >+ >+ $schema->storage->txn_rollback; >+}; >diff --git a/t/db_dependent/api/v1/preservation_Trains.t b/t/db_dependent/api/v1/preservation_Trains.t >new file mode 100755 >index 00000000000..6f73bd64076 >--- /dev/null >+++ b/t/db_dependent/api/v1/preservation_Trains.t >@@ -0,0 +1,525 @@ >+#!/usr/bin/env perl >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Test::More tests => 6; >+use Test::Mojo; >+ >+use t::lib::TestBuilder; >+use t::lib::Mocks; >+ >+use Koha::Preservation::Trains; >+use Koha::Database; >+ >+my $schema = Koha::Database->new->schema; >+my $builder = t::lib::TestBuilder->new; >+ >+my $t = Test::Mojo->new('Koha::REST::V1'); >+t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); >+ >+subtest 'list() tests' => sub { >+ >+ plan tests => 8; >+ >+ $schema->storage->txn_begin; >+ >+ Koha::Preservation::Trains->search->delete; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**30 } >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ ## Authorized user tests >+ # No trains, so empty array should be returned >+ $t->get_ok("//$userid:$password@/api/v1/preservation/trains")->status_is(200) >+ ->json_is( [] ); >+ >+ my $train = $builder->build_object( >+ { >+ class => 'Koha::Preservation::Trains', >+ } >+ ); >+ >+ # One train created, should get returned >+ $t->get_ok("//$userid:$password@/api/v1/preservation/trains")->status_is(200) >+ ->json_is( [ $train->to_api ] ); >+ >+ # Unauthorized access >+ $t->get_ok("//$unauth_userid:$password@/api/v1/preservation/trains") >+ ->status_is(403); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'get() tests' => sub { >+ >+ plan tests => 14; >+ >+ $schema->storage->txn_begin; >+ >+ my $train = >+ $builder->build_object( { class => 'Koha::Preservation::Trains' } ); >+ my $default_processing = $train->default_processing; >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**30 } >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ # This train exists, should get returned >+ $t->get_ok( "//$userid:$password@/api/v1/preservation/trains/" >+ . $train->train_id )->status_is(200) >+ ->json_is( $train->to_api ); >+ >+ # Return one train with some embeds >+ $t->get_ok( "//$userid:$password@/api/v1/preservation/trains/" >+ . $train->train_id => {'x-koha-embed' => 'items,default_processing'} )->status_is(200) >+ ->json_is( { %{ $train->to_api }, items => [], default_processing => $default_processing->unblessed }); >+ >+ # Return one train with all embeds >+ $t->get_ok( "//$userid:$password@/api/v1/preservation/trains/" >+ . $train->train_id => { 'x-koha-embed' => 'items,items.attributes,items.attributes.processing_attribute,default_processing,default_processing.attributes' } ) >+ ->status_is(200)->json_is( { %{ $train->to_api }, items => [], default_processing => { %{ $default_processing->unblessed }, attributes => [] } } ); >+ >+ # Unauthorized access >+ $t->get_ok( "//$unauth_userid:$password@/api/v1/preservation/trains/" >+ . $train->train_id )->status_is(403); >+ >+ # Attempt to get non-existent train >+ my $train_to_delete = >+ $builder->build_object( { class => 'Koha::Preservation::Trains' } ); >+ my $non_existent_id = $train_to_delete->train_id; >+ $train_to_delete->delete; >+ >+ $t->get_ok("//$userid:$password@/api/v1/preservation/trains/$non_existent_id") >+ ->status_is(404)->json_is( '/error' => 'Train not found' ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'add() tests' => sub { >+ >+ plan tests => 18; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**30 } >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ my $default_processing = $builder->build_object({class => 'Koha::Preservation::Processings'}); >+ my $train = { >+ name => "train name", >+ description => "train description", >+ default_processing_id => $default_processing->processing_id, >+ not_for_loan => 42, >+ }; >+ >+ # Unauthorized attempt to write >+ $t->post_ok( "//$unauth_userid:$password@/api/v1/preservation/trains" => json => >+ $train )->status_is(403); >+ >+ # Authorized attempt to write invalid data >+ my $train_with_invalid_field = { >+ blah => "train Blah", >+ %$train, >+ }; >+ >+ $t->post_ok( "//$userid:$password@/api/v1/preservation/trains" => json => >+ $train_with_invalid_field )->status_is(400)->json_is( >+ "/errors" => [ >+ { >+ message => "Properties not allowed: blah.", >+ path => "/body" >+ } >+ ] >+ ); >+ >+ # Authorized attempt to write >+ my $train_id = >+ $t->post_ok( >+ "//$userid:$password@/api/v1/preservation/trains" => json => $train ) >+ ->status_is( 201, 'SWAGGER3.2.1' )->header_like( >+ Location => qr|^/api/v1/preservation/trains/\d*|, >+ 'SWAGGER3.4.1' >+ )->json_is( '/name' => $train->{name} ) >+ ->json_is( '/description' => $train->{description} ) >+ ->json_is( '/default_processing_id' => $train->{default_processing_id} ) >+ ->json_is( '/not_for_loan' => $train->{not_for_loan} ) >+ ->tx->res->json->{train_id}; >+ >+ # Authorized attempt to create with null id >+ $train->{train_id} = undef; >+ $t->post_ok( >+ "//$userid:$password@/api/v1/preservation/trains" => json => $train ) >+ ->status_is(400)->json_has('/errors'); >+ >+ # Authorized attempt to create with existing id >+ $train->{train_id} = $train_id; >+ $t->post_ok( >+ "//$userid:$password@/api/v1/preservation/trains" => json => $train ) >+ ->status_is(400)->json_is( >+ "/errors" => [ >+ { >+ message => "Read-only.", >+ path => "/body/train_id" >+ } >+ ] >+ ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'update() tests' => sub { >+ >+ plan tests => 15; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**30 } >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ my $train_id = >+ $builder->build_object( { class => 'Koha::Preservation::Trains' } )->train_id; >+ >+ # Unauthorized attempt to update >+ $t->put_ok( >+ "//$unauth_userid:$password@/api/v1/preservation/trains/$train_id" => >+ json => { name => 'New unauthorized name change' } )->status_is(403); >+ >+ # Attempt partial update on a PUT >+ my $train_with_missing_field = { >+ }; >+ >+ $t->put_ok( >+ "//$userid:$password@/api/v1/preservation/trains/$train_id" => json => >+ $train_with_missing_field )->status_is(400) >+ ->json_is( "/errors" => >+ [ { message => "Missing property.", path => "/body/name" } ] ); >+ >+ my $default_processing = $builder->build_object({class => 'Koha::Preservation::Processings'}); >+ # Full object update on PUT >+ my $train_with_updated_field = { >+ name => "New name", >+ description => "train description", >+ default_processing_id => $default_processing->processing_id, >+ not_for_loan => 42, >+ }; >+ >+ $t->put_ok( >+ "//$userid:$password@/api/v1/preservation/trains/$train_id" => json => >+ $train_with_updated_field )->status_is(200) >+ ->json_is( '/name' => 'New name' ); >+ >+ # Authorized attempt to write invalid data >+ my $train_with_invalid_field = { >+ blah => "train Blah", >+ %$train_with_updated_field, >+ }; >+ >+ $t->put_ok( >+ "//$userid:$password@/api/v1/preservation/trains/$train_id" => json => >+ $train_with_invalid_field )->status_is(400)->json_is( >+ "/errors" => [ >+ { >+ message => "Properties not allowed: blah.", >+ path => "/body" >+ } >+ ] >+ ); >+ >+ # Attempt to update non-existent train >+ my $train_to_delete = >+ $builder->build_object( { class => 'Koha::Preservation::Trains' } ); >+ my $non_existent_id = $train_to_delete->train_id; >+ $train_to_delete->delete; >+ >+ $t->put_ok( "//$userid:$password@/api/v1/preservation/trains/$non_existent_id" => >+ json => $train_with_updated_field )->status_is(404); >+ >+ # Wrong method (POST) >+ $train_with_updated_field->{train_id} = 2; >+ >+ $t->post_ok( >+ "//$userid:$password@/api/v1/preservation/trains/$train_id" => json => >+ $train_with_updated_field )->status_is(404); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'delete() tests' => sub { >+ >+ plan tests => 7; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**30 } >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+ ); >+ >+ $patron->set_password( { password => $password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ my $train_id = >+ $builder->build_object( { class => 'Koha::Preservation::Trains' } )->train_id; >+ >+ # Unauthorized attempt to delete >+ $t->delete_ok( >+ "//$unauth_userid:$password@/api/v1/preservation/trains/$train_id") >+ ->status_is(403); >+ >+ # Delete existing train >+ $t->delete_ok("//$userid:$password@/api/v1/preservation/trains/$train_id") >+ ->status_is( 204, 'SWAGGER3.2.4' )->content_is( '', 'SWAGGER3.3.4' ); >+ >+ # Attempt to delete non-existent train >+ $t->delete_ok("//$userid:$password@/api/v1/preservation/trains/$train_id") >+ ->status_is(404); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest '*_item() tests' => sub { >+ >+ plan tests => 26; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**30 } >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $not_for_loan_waiting_list_in = 24; >+ my $not_for_loan_train_in = 42; >+ >+ t::lib::Mocks::mock_preference( 'PreservationNotForLoanWaitingListIn', >+ $not_for_loan_waiting_list_in ); >+ >+ my $default_processing = >+ $builder->build_object( { class => 'Koha::Preservation::Processings' } ); >+ my $another_processing = >+ $builder->build_object( { class => 'Koha::Preservation::Processings' } ); >+ >+ my $attributes = [ >+ { >+ name => 'color', >+ type => 'authorised_value', >+ option_source => 'COLORS' >+ }, >+ { name => 'title', type => 'db_column', option_source => '245$a' }, >+ { >+ name => 'height', >+ type => 'free_text' >+ }, >+ ]; >+ my $processing_attributes = $default_processing->attributes($attributes); >+ my $color_attribute = >+ $processing_attributes->search( { name => 'color' } )->next; >+ my $title_attribute = >+ $processing_attributes->search( { name => 'title' } )->next; >+ my $height_attribute = >+ $processing_attributes->search( { name => 'height' } )->next; >+ >+ my $train = $builder->build_object( >+ { >+ class => 'Koha::Preservation::Trains', >+ value => { >+ not_for_loan => $not_for_loan_train_in, >+ default_processing_id => $default_processing->processing_id, >+ closed_on => undef, >+ sent_on => undef, >+ received_on => undef, >+ } >+ } >+ ); >+ my $train_id = $train->train_id; >+ >+ my $item_1 = $builder->build_sample_item; >+ my $item_2 = $builder->build_sample_item; >+ my $item_3 = $builder->build_sample_item; >+ >+ # Add item not in waiting list >+ $t->post_ok( >+ "//$userid:$password@/api/v1/preservation/trains/$train_id/items" => >+ json => { item_id => $item_1->itemnumber } )->status_is(400) >+ ->json_is( { error => 'Item not in waiting list' } ); >+ >+ $item_1->notforloan($not_for_loan_waiting_list_in)->store; >+ >+ # Add item in waiting list >+ my $item_attributes = [ >+ { >+ processing_attribute_id => $color_attribute->processing_attribute_id, >+ value => 'red' >+ }, >+ { >+ processing_attribute_id => $title_attribute->processing_attribute_id, >+ value => 'my title' >+ }, >+ ]; >+ my $train_item_id = >+ $t->post_ok( >+ "//$userid:$password@/api/v1/preservation/trains/$train_id/items" => >+ json => >+ { item_id => $item_1->itemnumber, attributes => $item_attributes } ) >+ ->status_is( 201, 'SWAGGER3.2.1' ) >+ ->json_is( '/item_id' => $item_1->itemnumber ) >+ ->json_is( '/processing_id' => $train->default_processing_id ) >+ ->json_has('/added_on')->tx->res->json->{train_item_id}; >+ my $train_item = Koha::Preservation::Train::Items->find($train_item_id); >+ >+ $t->get_ok( >+ "//$userid:$password@/api/v1/preservation/trains/$train_id/items/$train_item_id" >+ => { 'x-koha-embed' => 'attributes' } )->status_is(200)->json_is( >+ { >+ %{ $train_item->to_api }, >+ attributes => $train_item->attributes->to_api >+ } >+ ); >+ >+ is( $item_1->get_from_storage->notforloan, >+ $train->not_for_loan, >+ "Item not for loan has been set to train's not for loan" ); >+ >+ # Add deleted item >+ $item_2->delete; >+ $t->post_ok( >+ "//$userid:$password@/api/v1/preservation/trains/$train_id/items" => >+ json => { item_id => $item_2->itemnumber } )->status_is(404) >+ ->json_is( { error => 'Item not found' } ); >+ >+ # Update item >+ my $new_item_attributes = [ >+ { >+ processing_attribute_id => $title_attribute->processing_attribute_id, >+ value => 'my new title' >+ }, >+ { >+ processing_attribute_id => $height_attribute->processing_attribute_id, >+ value => '24cm' >+ }, >+ ]; >+ $t->put_ok( >+ "//$userid:$password@/api/v1/preservation/trains/$train_id/items/$train_item_id" >+ => json => { >+ item_id => $item_1->itemnumber, >+ attributes => $new_item_attributes, >+ } >+ )->status_is(200); >+ >+ $t->get_ok( >+ "//$userid:$password@/api/v1/preservation/trains/$train_id/items/$train_item_id" >+ => { 'x-koha-embed' => 'attributes' } )->status_is(200)->json_is( >+ { >+ %{ $train_item->to_api }, >+ attributes => $train_item->attributes->to_api >+ } >+ ); >+ >+ # Delete existing item >+ $t->delete_ok("//$userid:$password@/api/v1/preservation/trains/$train_id/items/$train_item_id") >+ ->status_is( 204, 'SWAGGER3.2.4' )->content_is( '', 'SWAGGER3.3.4' ); >+ >+ # Delete non existing item >+ $t->delete_ok("//$userid:$password@/api/v1/preservation/trains/$train_id/items/$train_item_id") >+ ->status_is(404)->json_is( '/error' => 'Train item not found' ); >+ >+ $schema->storage->txn_rollback; >+}; >diff --git a/t/db_dependent/api/v1/preservation_WaitingList.t b/t/db_dependent/api/v1/preservation_WaitingList.t >new file mode 100755 >index 00000000000..4575b3c6b0d >--- /dev/null >+++ b/t/db_dependent/api/v1/preservation_WaitingList.t >@@ -0,0 +1,167 @@ >+#!/usr/bin/env perl >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Test::More tests => 1; >+use Test::Mojo; >+use Test::Warn; >+ >+use t::lib::TestBuilder; >+use t::lib::Mocks; >+ >+use Koha::Preservation::Trains; >+use Koha::Database; >+use Koha::DateUtils qw( dt_from_string ); >+ >+my $schema = Koha::Database->new->schema; >+my $builder = t::lib::TestBuilder->new; >+ >+my $t = Test::Mojo->new('Koha::REST::V1'); >+t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); >+ >+subtest 'add_item, list, remove_item tests' => sub { >+ >+ plan tests => 34; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**30 } >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $item_1 = $builder->build_sample_item; >+ my $item_2 = $builder->build_sample_item; >+ my $item_3 = $builder->build_sample_item; >+ >+ t::lib::Mocks::mock_preference( 'PreservationNotForLoanWaitingListIn', undef ); >+ $t->get_ok("//$userid:$password@/api/v1/preservation/waiting-list/items") >+ ->status_is(400)->json_is( >+ { >+ error => 'MissingSettings', >+ parameter => 'PreservationNotForLoanWaitingListIn' >+ } >+ ); >+ >+ my $not_for_loan_waiting_list_in = 24; >+ t::lib::Mocks::mock_preference( 'PreservationNotForLoanWaitingListIn', >+ $not_for_loan_waiting_list_in ); >+ >+ $t->get_ok("//$userid:$password@/api/v1/preservation/waiting-list/items") >+ ->status_is(200)->json_is( [] ); >+ >+ # Add items >+ $t->post_ok( >+ "//$userid:$password@/api/v1/preservation/waiting-list/items" => >+ json => [ >+ { item_id => $item_1->itemnumber }, >+ { barcode => $item_3->barcode } >+ ] >+ )->status_is(201)->json_is( >+ [ >+ { item_id => $item_1->itemnumber }, >+ { item_id => $item_3->itemnumber } >+ ] >+ ); >+ >+ is( $item_1->get_from_storage->notforloan, $not_for_loan_waiting_list_in ); >+ is( $item_2->get_from_storage->notforloan, 0 ); >+ is( $item_3->get_from_storage->notforloan, $not_for_loan_waiting_list_in ); >+ >+ $t->post_ok( >+ "//$userid:$password@/api/v1/preservation/waiting-list/items" => >+ json => [ >+ { item_id => $item_2->itemnumber }, >+ { barcode => $item_3->barcode } >+ ] >+ )->status_is(201)->json_is( [ { item_id => $item_2->itemnumber } ] ); >+ >+ is( $item_1->get_from_storage->notforloan, $not_for_loan_waiting_list_in ); >+ is( $item_2->get_from_storage->notforloan, $not_for_loan_waiting_list_in ); >+ is( $item_3->get_from_storage->notforloan, $not_for_loan_waiting_list_in ); >+ >+ $t->delete_ok( >+ "//$userid:$password@/api/v1/preservation/waiting-list/items/" >+ . $item_2->itemnumber )->status_is(204); >+ >+ is( $item_2->get_from_storage->notforloan, 0, >+ "Item removed from the waiting list has its notforloan status back to 0" >+ ); >+ >+ # Add item_1 to a non-received train >+ my $train = $builder->build_object( >+ { >+ class => 'Koha::Preservation::Trains', >+ value => { >+ not_for_loan => 42, >+ closed_on => undef, >+ sent_on => undef, >+ received_on => undef >+ } >+ } >+ ); >+ $train->add_item( { item_id => $item_1->itemnumber } ); >+ >+ # And try to add it to the waiting list >+ warning_like { >+ $t->post_ok( >+ "//$userid:$password@/api/v1/preservation/waiting-list/items" => >+ json => [ { item_id => $item_1->itemnumber } ] )->status_is(201) >+ ->json_is( [] ); >+ } >+ qr[Cannot add item to waiting list, it is already in a non-received train]; >+ >+ # Add item_3 to a train, receive the train and add the item to the waiting list >+ $train = $builder->build_object( >+ { >+ class => 'Koha::Preservation::Trains', >+ value => { >+ not_for_loan => 42, >+ closed_on => undef, >+ sent_on => undef, >+ received_on => undef >+ } >+ } >+ ); >+ $train->add_item( { item_id => $item_3->itemnumber } ); >+ $train->received_on(dt_from_string)->store; >+ >+ $t->post_ok( >+ "//$userid:$password@/api/v1/preservation/waiting-list/items" => >+ json => [ { item_id => $item_3->itemnumber } ] )->status_is(201) >+ ->json_is( [ { item_id => $item_3->itemnumber } ] ); >+ >+ $t->delete_ok( >+ "//$userid:$password@/api/v1/preservation/waiting-list/items/" >+ . $item_2->itemnumber )->status_is(204); >+ >+ $item_2->delete; >+ $t->delete_ok("//$userid:$password@/api/v1/preservation/waiting-list/items") >+ ->status_is(404); >+ >+ $t->delete_ok( >+ "//$userid:$password@/api/v1/preservation/waiting-list/items/" >+ . $item_2->itemnumber )->status_is(404); >+ >+ $schema->storage->txn_rollback; >+}; >-- >2.25.1
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 30708
:
134715
|
134716
|
134732
|
134733
|
134734
|
134736
|
134737
|
134738
|
134782
|
134783
|
134784
|
134785
|
152939
|
152940
|
152941
|
152942
|
152943
|
152944
|
152945
|
152946
|
152947
|
152948
|
152949
|
152950
|
152951
|
152952
|
152953
|
152954
|
152955
|
152956
|
152957
|
152958
|
152959
|
152960
|
152961
|
152962
|
152963
|
152964
|
152965
|
152966
|
152967
|
152968
|
152969
|
152970
|
153102
|
153576
|
154167
|
154168
|
154169
|
154170
|
154171
|
154172
|
154173
|
154174
|
154175
|
154176
|
154177
|
154178
|
154179
|
154180
|
154181
|
154182
|
154183
|
154184
|
154185
|
154186
|
154187
|
154188
|
154189
|
154190
|
154191
|
154192
|
154193
|
154194
|
154195
|
154196
|
154197
|
154198
|
154199
|
154200
|
154201
|
154202
|
154203
|
156592
|
156596
|
156597
|
156636
|
156637
|
156638
|
156639
|
156640
|
156641
|
156642
|
156643
|
156644
|
156645
|
156646
|
156647
|
156648
|
156649
|
156650
|
156651
|
156652
|
156653
|
156654
|
156655
|
156656
|
156657
|
156658
|
156659
|
156660
|
156661
|
156662
|
156663
|
156664
|
156665
|
156666
|
156667
|
156668
|
156669
|
156670
|
156671
|
156672
|
156673
|
156674
|
156675
|
157502
|
157503
|
157506
|
157508
|
157512
|
157514
|
157557