diff --git a/apache/000-default.conf b/apache/000-default.conf index ff091912a..66fef5891 100644 --- a/apache/000-default.conf +++ b/apache/000-default.conf @@ -48,6 +48,8 @@ Listen 8080 RewriteRule ^/mock-holdings-api/v1/item_access /htapps/babel/mock-holdings-api/web/v1/item_access.not_held [L] RewriteCond %{QUERY_STRING} item_id=test.ic_not_current RewriteRule ^/mock-holdings-api/v1/item_access /htapps/babel/mock-holdings-api/web/v1/item_access.not_current [L] + RewriteCond %{QUERY_STRING} item_id=test.op_brlm + RewriteRule ^/mock-holdings-api/v1/item_access /htapps/babel/mock-holdings-api/web/v1/item_access.brlm [L] ### IMGSRV PROXY diff --git a/bin/run_playwright_tests.sh b/bin/run_playwright_tests.sh index e32be8e53..97fdb4c77 100755 --- a/bin/run_playwright_tests.sh +++ b/bin/run_playwright_tests.sh @@ -13,13 +13,14 @@ switch_auth() { echo -e "${color_cyan}Configuring mocked holdings API${color_reset}" for app in pt imgsrv ssd; do - echo "holdings_api_url = http://apache-test:8080/mock-holdings-api" >> $babel_home/$app/lib/Config/local.conf + echo "holdings_api_url = http://apache:8080/mock-holdings-api" >> $babel_home/$app/lib/Config/local.conf done - echo -e "Resetting ht_sessions database table " - docker compose exec mysql-sdr mariadb -u mdp-lib -pmdp-lib -h localhost ht -e "DELETE FROM ht_sessions;" + echo -e "Resetting ht_sessions & pt_exclusivity_ng database tables" + docker compose exec mysql-sdr mariadb -u mdp-lib -pmdp-lib -h localhost ht -e "DELETE FROM ht_sessions; DELETE FROM pt_exclusivity_ng;" echo -e "Reloading Apache configuration" - docker compose exec apache-test kill -USR1 1 + docker compose exec apache kill -HUP 1 + sleep 1; } run_or_exit() { @@ -43,7 +44,7 @@ run_test() { else # Run tests for the given kind of authenticated user switch_auth $usertype - run_or_exit docker compose run --rm playwright npx playwright test $usertype --trace on + run_or_exit docker compose run --rm playwright npx playwright test $usertype --trace on --config playwright_auth.config.js fi } @@ -52,6 +53,8 @@ if [ "$testset" == "all" ]; then run_test unauthed run_test ssd_user run_test resource_sharing_user + run_test emergency_access_affiliate + run_test library_ipaddr_user else run_test $testset fi diff --git a/common/docker/catalog.yml b/common/docker/catalog.yml new file mode 100644 index 000000000..c2b92ae64 --- /dev/null +++ b/common/docker/catalog.yml @@ -0,0 +1,30 @@ +# Optional service (not required for core functionality testing) +# As we want to test these via playwright, they could move into core.yml + +x-healthcheck-defaults: &healthcheck-defaults + interval: 5s + timeout: 10s + start_period: 10s + retries: 5 + +services: + vufind: + build: ../../catalog #TODO: Use the built unstable container + hostname: 'catalog-dev' + volumes: + - ${BABEL_HOME}/catalog:/app + - ${BABEL_HOME}/firebird-common:/htapps/babel/firebird-common + - ${BABEL_HOME}/catalog/conf/authspecs-sample.yaml:/app/conf/authspecs.yaml + environment: + - FIREBIRD_HOME=/htapps/babel/firebird-common + depends_on: + mysql-sdr: + condition: service_healthy + solr-sdr-catalog: + condition: service_healthy + healthcheck: + interval: 5s + timeout: 10s + start_period: 10s + retries: 5 + test: "service php8.4-fpm status | grep running" diff --git a/common/docker/core.yml b/common/docker/core.yml index 7e7c22e9b..0164640e1 100644 --- a/common/docker/core.yml +++ b/common/docker/core.yml @@ -1,3 +1,5 @@ +# Services required for minimum babel functionality & used by tests + x-healthcheck-defaults: &healthcheck-defaults interval: 5s timeout: 10s @@ -19,31 +21,6 @@ services: retries: 5 test: "curl -f --retry-connrefused http://localhost:9033" - solr-lss-dev: - # This is another core service - image: solr:6.6.6-alpine - ports: - - "8983:8983" - user: ${CURRENT_USER} - volumes: - - ${BABEL_HOME}/lss_solr_configs/solr6_standalone/lss-dev/core-x:/opt/solr/server/solr/core-x - - ${BABEL_HOME}/lss_solr_configs/solr6_standalone/lss-dev/core-y:/opt/solr/server/solr/core-y - - ${BABEL_HOME}/lss_solr_configs/solr6_standalone:/opt/lss_solr_configs - - ${BABEL_HOME}/lss_solr_configs/solr6_standalone/lib:/opt/solr/server/solr/lib - - ${BABEL_HOME}/logs/solr:/opt/solr/server/logs - healthcheck: - <<: *healthcheck-defaults - test: "ping -c 1 solr-lss-dev:8983" - - solr-ptsearch: - # This is a core service - build: ../../ptsearch-solr #TODO: Build from image in ghcr.io - ports: - - "8984:8983" - healthcheck: - <<: *healthcheck-defaults - test: "curl -f --retry-connrefused http://solr-ptsearch:8983/solr/ptsearch/admin/ping" - mysql-sdr: # This is a core service image: ghcr.io/hathitrust/db-image:latest @@ -96,28 +73,6 @@ services: retries: 5 test: "cgi-fcgi -bind -connect 127.0.0.1:31028" - vufind: - # A core service - build: ../../catalog #TODO: Use the built unstable container - hostname: 'catalog-dev' - volumes: - - ${BABEL_HOME}/catalog:/app - - ${BABEL_HOME}/firebird-common:/htapps/babel/firebird-common - - ${BABEL_HOME}/catalog/conf/authspecs-sample.yaml:/app/conf/authspecs.yaml - environment: - - FIREBIRD_HOME=/htapps/babel/firebird-common - depends_on: - mysql-sdr: - condition: service_healthy - solr-sdr-catalog: - condition: service_healthy - healthcheck: - interval: 5s - timeout: 10s - start_period: 10s - retries: 5 - test: "service php7.4-fpm status | grep running" - volumes: mysql_sdr_data: solr_sdr_catalog: diff --git a/common/docker/solr.yml b/common/docker/solr.yml new file mode 100644 index 000000000..56ae6d01d --- /dev/null +++ b/common/docker/solr.yml @@ -0,0 +1,34 @@ +# Optional services (not required for core functionality testing) +# As we want to test these via playwright, they should move into core.yml + +x-healthcheck-defaults: &healthcheck-defaults + interval: 5s + timeout: 10s + start_period: 10s + retries: 5 + +services: + solr-lss-dev: + # This is another core service + image: solr:6.6.6-alpine + ports: + - "8983:8983" + user: ${CURRENT_USER} + volumes: + - ${BABEL_HOME}/lss_solr_configs/solr6_standalone/lss-dev/core-x:/opt/solr/server/solr/core-x + - ${BABEL_HOME}/lss_solr_configs/solr6_standalone/lss-dev/core-y:/opt/solr/server/solr/core-y + - ${BABEL_HOME}/lss_solr_configs/solr6_standalone:/opt/lss_solr_configs + - ${BABEL_HOME}/lss_solr_configs/solr6_standalone/lib:/opt/solr/server/solr/lib + - ${BABEL_HOME}/logs/solr:/opt/solr/server/logs + healthcheck: + <<: *healthcheck-defaults + test: "ping -c 1 solr-lss-dev:8983" + + solr-ptsearch: + # This is a core service + build: ../../ptsearch-solr #TODO: Build from image in ghcr.io + ports: + - "8984:8983" + healthcheck: + <<: *healthcheck-defaults + test: "curl -f --retry-connrefused http://solr-ptsearch:8983/solr/ptsearch/admin/ping" diff --git a/docker-compose.yml b/docker-compose.yml index 16376ad7a..7c3b6917f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: file: common/docker/playwright.yml service: playwright depends_on: - apache-test: + apache: condition: service_healthy profiles: - playwright_testing @@ -23,7 +23,6 @@ services: profiles: - playwright_testing - firebird: extends: file: common/docker/node.yml @@ -37,18 +36,6 @@ services: profiles: - frontend - apache-test: - extends: - file: common/docker/apache.yml - service: apache - depends_on: - mysql-sdr: - condition: service_healthy - solr-sdr-catalog: - condition: service_healthy - imgsrv: - condition: service_healthy - # Backend Services apache: extends: @@ -57,20 +44,15 @@ services: depends_on: mysql-sdr: condition: service_healthy - solr-lss-dev: - condition: service_healthy solr-sdr-catalog: condition: service_healthy - solr-ptsearch: - condition: service_healthy imgsrv: condition: service_healthy - vufind: - condition: service_healthy ports: - "8080:8080" profiles: - backend + - playwright_testing perl-test: extends: @@ -92,6 +74,28 @@ services: profiles: - backend +# Optional dependencies; not included in current playwright testing + vufind: + extends: + file: common/docker/catalog.yml + service: vufind + profiles: + - backend + + solr-lss-dev: + extends: + file: common/docker/solr.yml + service: solr-lss-dev + profiles: + - backend + + solr-ptsearch: + extends: + file: common/docker/solr.yml + service: solr-ptsearch + profiles: + - backend + # Indexing traject: extends: diff --git a/mdp-lib/PIFiller/Common/Globals.pm b/mdp-lib/PIFiller/Common/Globals.pm index 6d122b509..239def5ea 100644 --- a/mdp-lib/PIFiller/Common/Globals.pm +++ b/mdp-lib/PIFiller/Common/Globals.pm @@ -282,7 +282,7 @@ sub handle_ACCESS_HOLDINGS_PI } $s .= wrap_string_in_tag($held, 'Held'); } - elsif ($user_access_type eq 'in_library_user') { + elsif ($access_type eq 'in_library_user') { my $brittle_held = 'NO'; if (Access::Holdings::id_is_held_and_BRLM($C, $id, $inst)) { $brittle_held = 'YES'; diff --git a/mock-holdings-api/web/v1/item_access.brlm b/mock-holdings-api/web/v1/item_access.brlm new file mode 100644 index 000000000..ef4693087 --- /dev/null +++ b/mock-holdings-api/web/v1/item_access.brlm @@ -0,0 +1 @@ +{"brlm_count":1,"copy_count":1,"currently_held_count":1,"deposited":0,"format":"spm","n_enum":"","ocns":[123456]} diff --git a/pt/web/firebird/playwright.config.js b/pt/web/firebird/playwright.config.js index 53a5f7bcf..2f98c41cf 100644 --- a/pt/web/firebird/playwright.config.js +++ b/pt/web/firebird/playwright.config.js @@ -5,7 +5,7 @@ let testUrl; if (process.env.LOCAL == 'local') { testUrl = 'http://localhost:8080'; } else { - testUrl = 'http://apache-test:8080'; + testUrl = 'http://apache:8080'; } /** diff --git a/pt/web/firebird/playwright_auth.config.js b/pt/web/firebird/playwright_auth.config.js new file mode 100644 index 000000000..4bb0f76aa --- /dev/null +++ b/pt/web/firebird/playwright_auth.config.js @@ -0,0 +1,60 @@ +// @ts-check +import { defineConfig, devices } from '@playwright/test'; + +let testUrl; +if (process.env.LOCAL == 'local') { + testUrl = 'http://localhost:8080'; +} else { + testUrl = 'http://apache:8080'; +} + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// import dotenv from 'dotenv'; +// import path from 'path'; +// dotenv.config({ path: path.resolve(__dirname, '.env') }); + +/** + * @see https://playwright.dev/docs/test-configuration + */ +export default defineConfig({ + testDir: './tests', + testMatch: /(.+\.)?(test|spec)\.[jt]s/, + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry authed tests -- there may be occasional race conditions with apache reloading its config */ + retries: 2, + /* Don't run authed tests in parallel -- sessions may interfere with each other, esp. for ETAS / section 108 */ + workers: 1, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + // reporter: process.env.CI ? '["list", "html"]' : 'list', + reporter: [['list'], ['html', { open: 'never' }]], + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: testUrl, + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'retain-on-failure', + // trace: 'on', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run babel', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/pt/web/firebird/tests/auth_emergency_access_affiliate.spec.js b/pt/web/firebird/tests/auth_emergency_access_affiliate.spec.js new file mode 100644 index 000000000..3185c706e --- /dev/null +++ b/pt/web/firebird/tests/auth_emergency_access_affiliate.spec.js @@ -0,0 +1,37 @@ +import { expect, test } from '@playwright/test'; +import fs from 'fs'; + +test.describe('emergency_access_affiliate access to ic material', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/cgi/pt?id=test.pd_open'); + await page.getByRole('button', { name: 'Allow all cookies' }).click(); + }); + + test('pageturner loads image for ic_currently_held after checkout', async ({ page }) => { + await page.goto('/cgi/pt?id=test.ic_currently_held'); + + await expect(page.getByRole('figure')).toHaveCount(0); + await page.getByRole('link', { name: 'Check Out' }).click(); + await expect(page.getByRole('figure')).toBeVisible(); + await page.getByRole('link', { name: 'Return Early' }).click(); + await expect(page.getByRole('figure')).toHaveCount(0); + }); + + // resource sharing users cannot see lost/missing/withdrawn material + test('pageturner loads image for ic_not_current after checkout', async ({ page }) => { + await page.goto('/cgi/pt?id=test.ic_not_current'); + + await expect(page.getByRole('figure')).toHaveCount(0); + await page.getByRole('link', { name: 'Check Out' }).click(); + await expect(page.getByRole('figure')).toBeVisible(); + await page.getByRole('link', { name: 'Return Early' }).click(); + await expect(page.getByRole('figure')).toHaveCount(0); + }); + + test('pageturner does not load image or show checkout for ic_not_held', async ({ page }) => { + await page.goto('/cgi/pt?id=test.ic_not_held'); + await expect(page.getByText('not available online')).toHaveCount(1); + await expect(page.getByRole('link', { name: 'Check Out' })).toHaveCount(0); + await expect(page.getByRole('figure')).toHaveCount(0); + }); +}); diff --git a/pt/web/firebird/tests/auth_library_ipaddr_user.spec.js b/pt/web/firebird/tests/auth_library_ipaddr_user.spec.js new file mode 100644 index 000000000..0c5fed224 --- /dev/null +++ b/pt/web/firebird/tests/auth_library_ipaddr_user.spec.js @@ -0,0 +1,27 @@ +import { expect, test } from '@playwright/test'; +import fs from 'fs'; + +test.describe('in_library_user access to op/ipma material reported as brlm', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/cgi/pt?id=test.pd_open'); + await page.getByRole('button', { name: 'Allow all cookies' }).click(); + }); + + test('pageturner loads image for op_brlm after checkout', async ({ page }) => { + await page.goto('/cgi/pt?id=test.op_brlm'); + + await expect(page.getByRole('figure')).toHaveCount(0); + await page.getByRole('link', { name: 'Check Out' }).click(); + await expect(page.getByRole('figure')).toBeVisible(); + await page.getByRole('link', { name: 'Return Early' }).click(); + await expect(page.getByRole('figure')).toHaveCount(0); + }); + + // not op/ipma and not brlm + test('pageturner does not load image or show checkout for ic_currently_held', async ({ page }) => { + await page.goto('/cgi/pt?id=test.ic_currently_held'); + await expect(page.getByText('not available online')).toHaveCount(1); + await expect(page.getByRole('link', { name: 'Check Out' })).toHaveCount(0); + await expect(page.getByRole('figure')).toHaveCount(0); + }); +}); diff --git a/pt/web/firebird/tests/imgsrv_download.spec.js b/pt/web/firebird/tests/imgsrv_download.spec.js index e803457da..9e5e2c8c6 100644 --- a/pt/web/firebird/tests/imgsrv_download.spec.js +++ b/pt/web/firebird/tests/imgsrv_download.spec.js @@ -12,7 +12,7 @@ test.describe('imgsrv download', () => { var currentTime = new Date().getTime(); const initialResponse = await request.get( - 'http://apache-test:8080/cgi/imgsrv/download/pdf?id=test.pd_open&callback=tunnelCallback&_=' + currentTime + 'http://apache:8080/cgi/imgsrv/download/pdf?id=test.pd_open&callback=tunnelCallback&_=' + currentTime ); const initialBody = await initialResponse.text(); @@ -33,7 +33,7 @@ test.describe('imgsrv download', () => { let done = false; while (done == false) { - const callbackResponse = await request.get('http://apache-test:8080' + callbackUrl); + const callbackResponse = await request.get('http://apache:8080' + callbackUrl); const callbackJson = await callbackResponse.json(); if (callbackJson.status == 'DONE') { @@ -47,7 +47,7 @@ test.describe('imgsrv download', () => { } } - const downloadResponse = await request.get('http://apache-test:8080' + downloadUrl); + const downloadResponse = await request.get('http://apache:8080' + downloadUrl); const downloadHeaders = downloadResponse.headers(); const downloadBody = await downloadResponse.text(); @@ -61,7 +61,7 @@ test.describe('imgsrv download', () => { var currentTime = new Date().getTime(); const initialResponse = await request.get( - 'http://apache-test:8080/cgi/imgsrv/download/epub?id=test.pd_open&callback=tunnelCallback&_=' + currentTime + 'http://apache:8080/cgi/imgsrv/download/epub?id=test.pd_open&callback=tunnelCallback&_=' + currentTime ); const initialBody = await initialResponse.text(); @@ -79,7 +79,7 @@ test.describe('imgsrv download', () => { let done = false; while (done == false) { - const callbackResponse = await request.get('http://apache-test:8080' + callbackUrl); + const callbackResponse = await request.get('http://apache:8080' + callbackUrl); const callbackJson = await callbackResponse.json(); if (callbackJson.status == 'DONE') { @@ -93,7 +93,7 @@ test.describe('imgsrv download', () => { } } - const downloadResponse = await request.get('http://apache-test:8080' + downloadUrl); + const downloadResponse = await request.get('http://apache:8080' + downloadUrl); const downloadHeaders = downloadResponse.headers(); const downloadBody = await downloadResponse.text(); @@ -107,7 +107,7 @@ test.describe('imgsrv download', () => { // no callback tunnel on single tiff const downloadResponse = await request.get( - 'http://apache-test:8080/cgi/imgsrv/image?id=test.pd_open&attachment=1&tracker=D1&format=image/tiff&size=full&seq=1' + 'http://apache:8080/cgi/imgsrv/image?id=test.pd_open&attachment=1&tracker=D1&format=image/tiff&size=full&seq=1' ); const downloadHeaders = downloadResponse.headers(); const downloadBody = await downloadResponse.text(); @@ -122,7 +122,7 @@ test.describe('imgsrv download', () => { //no callback tunnel on single pages const downloadResponse = await request.get( - 'http://apache-test:8080/cgi/imgsrv/image?id=test.pd_open&attachment=1&tracker=D1&format=image/jpeg&size=ppi:300&seq=2' + 'http://apache:8080/cgi/imgsrv/image?id=test.pd_open&attachment=1&tracker=D1&format=image/jpeg&size=ppi:300&seq=2' ); const downloadHeaders = downloadResponse.headers(); const downloadBody = await downloadResponse.text(); @@ -136,7 +136,7 @@ test.describe('imgsrv download', () => { //no callback tunnel on non-tiff selections <11 pages const downloadResponse = await request.get( - 'http://apache-test:8080/cgi/imgsrv/image?id=test.pd_open&attachment=1&tracker=D1&format=image/jpeg&target_ppi=0&seq=1&seq=2' + 'http://apache:8080/cgi/imgsrv/image?id=test.pd_open&attachment=1&tracker=D1&format=image/jpeg&target_ppi=0&seq=1&seq=2' ); const downloadHeaders = downloadResponse.headers(); const downloadBody = await downloadResponse.text(); diff --git a/setup.sh b/setup.sh index af2c14029..1d7e57356 100755 --- a/setup.sh +++ b/setup.sh @@ -39,10 +39,10 @@ CURRENT_USER="$(id -u):$(id -g)" APACHE_RUN_USER="$(id -u)" APACHE_RUN_GROUP="$(id -g)" BABEL_HOME="$(dirname $(realpath $0))" +EOT -ln -rsv geoip/always_us.mmdb geoip/GeoIP2-Country.mmdb +ln -sfv always_us.mmdb geoip/GeoIP2-Country.mmdb -EOT echo echo 💎 Setting up stage_item... diff --git a/sql/sample_rights.sql b/sql/sample_rights.sql index eb1af52ea..c8681101f 100644 --- a/sql/sample_rights.sql +++ b/sql/sample_rights.sql @@ -2,6 +2,7 @@ REPLACE INTO ht.rights_current (namespace, id, attr, reason, source, access_profile, user, note) values ('test','ic_currently_held','2','1','19','1','babel','Synthetic test item'); REPLACE INTO ht.rights_current (namespace, id, attr, reason, source, access_profile, user, note) values ('test','ic_not_held','2','1','19','1','babel','Synthetic test item'); REPLACE INTO ht.rights_current (namespace, id, attr, reason, source, access_profile, user, note) values ('test','ic_not_current','2','1','19','1','babel','Synthetic test item'); +REPLACE INTO ht.rights_current (namespace, id, attr, reason, source, access_profile, user, note) values ('test','op_brlm','3','10','19','1','babel','Synthetic test item'); --- sample data for unit testing und-world which has an impoverished set of entries in db-image REPLACE INTO ht.rights_current (namespace, id, attr, reason, source, access_profile, user, note) values ('test','und-world_open','18','1','19','1','babel','Synthetic test item'); diff --git a/switch_auth.sh b/switch_auth.sh index 58e7ac219..f6206182b 100755 --- a/switch_auth.sh +++ b/switch_auth.sh @@ -48,16 +48,16 @@ echo echo -e "${color_cyan}Using auth file \x1B[37m$auth_file${color_reset}" cp -v $auth_file $auth_path/active_auth.conf echo -e "${color_cyan}Setting local development mode${color_reset}" -docker compose up -d apache-test -docker compose exec apache-test bash -c "perl mdp-lib/bin/debug.pl --enable > /dev/null" +docker compose up -d apache +docker compose exec apache bash -c "perl mdp-lib/bin/debug.pl --enable > /dev/null" echo -e "${color_cyan}Configuring mocked holdings API${color_reset}" for app in pt imgsrv ssd; do - echo "holdings_api_url = http://apache-test:8080/mock-holdings-api" >> $babel_home/$app/lib/Config/local.conf + echo "holdings_api_url = http://apache:8080/mock-holdings-api" >> $babel_home/$app/lib/Config/local.conf done echo -e "${color_cyan}Resetting ht_sessions database table ${color_reset}" docker compose exec mysql-sdr mariadb -vv -u mdp-lib -pmdp-lib -h localhost ht -e "DELETE FROM ht_sessions;" echo -e "${color_cyan}Reloading Apache configuration${color_reset}" -docker compose exec apache-test kill -USR1 1 +docker compose exec apache kill -HUP 1 echo -e "🎉 ${color_cyan} Done!${color_reset}"