openvibers/openvibe.tours Commits

The cities job reads a committed list (production had no cities) (#1)

OP OpenVibers committed 13 days ago · 439117462e · 4 files, +136 −1

* The cities job reads a committed list (seed/top-cities.json): data/ is the dev database's folder and not in git, so production had no cities

* CI: the shared test and security workflows, as every room has (the repo had none)

Changes

ddiff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
nnew file mode 100644
iindex 0000000..e3cdaae
---- /dev/null
++++ b/.github/workflows/ci.yml
@@@ -0,0 +1,17 @@
++name: ci
++on:
++ push:
++ branches: [main]
++ pull_request:
++jobs:
++ # install, node --check and npm test: the shared test job (Track Q).
++ test:
++ uses: OpenVibers/OpenVibe.Shared/.github/workflows/test.yml@423fe519b5fe9594cc81b670e5ba5cb9f21f6176
++ with:
++ syntax-dirs: server test scripts
++ # ADR-035: PostgreSQL 18 + PgBouncer (transaction mode) + Valkey 9 containers (openvibe-sdk
++ # scripts/test-services.sh). npm test runs every file on PGlite; test:pg runs them all through PgBouncer.
++ test: 'eval "$(node_modules/openvibe-sdk/scripts/test-services.sh up)" && npm test && npm run test:pg'
++ # Secret scan (gitleaks) and dependency audit, shared by every OpenVibers repo (Track Q).
++ security:
++ uses: OpenVibers/OpenVibe.Shared/.github/workflows/security.yml@423fe519b5fe9594cc81b670e5ba5cb9f21f6176
ddiff --git a/seed/top-cities.json b/seed/top-cities.json
nnew file mode 100644
iindex 0000000..e42d225
---- /dev/null
++++ b/seed/top-cities.json
@@@ -0,0 +1,102 @@
++[
++ { "name": "Tokyo", "country": "Japan" },
++ { "name": "Delhi", "country": "India" },
++ { "name": "Shanghai", "country": "China" },
++ { "name": "Dhaka", "country": "Bangladesh" },
++ { "name": "Cairo", "country": "Egypt" },
++ { "name": "Mumbai", "country": "India" },
++ { "name": "Beijing", "country": "China" },
++ { "name": "Osaka", "country": "Japan" },
++ { "name": "New York City", "country": "United States", "wikipedia": "New York City" },
++ { "name": "Karachi", "country": "Pakistan" },
++ { "name": "Buenos Aires", "country": "Argentina" },
++ { "name": "Chongqing", "country": "China" },
++ { "name": "Istanbul", "country": "Turkey" },
++ { "name": "Kolkata", "country": "India" },
++ { "name": "Manila", "country": "Philippines" },
++ { "name": "Lagos", "country": "Nigeria" },
++ { "name": "Rio de Janeiro", "country": "Brazil" },
++ { "name": "Tianjin", "country": "China" },
++ { "name": "Kinshasa", "country": "Democratic Republic of the Congo" },
++ { "name": "Guangzhou", "country": "China" },
++ { "name": "Los Angeles", "country": "United States" },
++ { "name": "Moscow", "country": "Russia" },
++ { "name": "Shenzhen", "country": "China" },
++ { "name": "Lahore", "country": "Pakistan" },
++ { "name": "Bangalore", "country": "India", "wikipedia": "Bangalore" },
++ { "name": "Paris", "country": "France" },
++ { "name": "Bogotá", "country": "Colombia" },
++ { "name": "Jakarta", "country": "Indonesia" },
++ { "name": "Chennai", "country": "India" },
++ { "name": "Lima", "country": "Peru" },
++ { "name": "Bangkok", "country": "Thailand" },
++ { "name": "Seoul", "country": "South Korea" },
++ { "name": "Nagoya", "country": "Japan" },
++ { "name": "Hyderabad", "country": "India" },
++ { "name": "London", "country": "United Kingdom" },
++ { "name": "Tehran", "country": "Iran" },
++ { "name": "Chicago", "country": "United States" },
++ { "name": "Chengdu", "country": "China" },
++ { "name": "Nanjing", "country": "China" },
++ { "name": "Wuhan", "country": "China" },
++ { "name": "Ho Chi Minh City", "country": "Vietnam" },
++ { "name": "Luanda", "country": "Angola" },
++ { "name": "Ahmedabad", "country": "India" },
++ { "name": "Kuala Lumpur", "country": "Malaysia" },
++ { "name": "Hong Kong", "country": "China" },
++ { "name": "Riyadh", "country": "Saudi Arabia" },
++ { "name": "Baghdad", "country": "Iraq" },
++ { "name": "Santiago", "country": "Chile" },
++ { "name": "Surat", "country": "India" },
++ { "name": "Madrid", "country": "Spain" },
++ { "name": "Suzhou", "country": "China" },
++ { "name": "Pune", "country": "India" },
++ { "name": "Harbin", "country": "China" },
++ { "name": "Houston", "country": "United States" },
++ { "name": "Dallas", "country": "United States" },
++ { "name": "Toronto", "country": "Canada" },
++ { "name": "Dar es Salaam", "country": "Tanzania" },
++ { "name": "Miami", "country": "United States" },
++ { "name": "Belo Horizonte", "country": "Brazil" },
++ { "name": "Singapore", "country": "Singapore" },
++ { "name": "Philadelphia", "country": "United States" },
++ { "name": "Atlanta", "country": "United States" },
++ { "name": "Fukuoka", "country": "Japan" },
++ { "name": "Khartoum", "country": "Sudan" },
++ { "name": "Barcelona", "country": "Spain" },
++ { "name": "Johannesburg", "country": "South Africa" },
++ { "name": "Saint Petersburg", "country": "Russia" },
++ { "name": "Qingdao", "country": "China" },
++ { "name": "Dalian", "country": "China" },
++ { "name": "Washington, D.C.", "country": "United States" },
++ { "name": "Yangon", "country": "Myanmar" },
++ { "name": "Alexandria", "country": "Egypt" },
++ { "name": "Jeddah", "country": "Saudi Arabia" },
++ { "name": "Hanoi", "country": "Vietnam" },
++ { "name": "Sydney", "country": "Australia" },
++ { "name": "Guadalajara", "country": "Mexico" },
++ { "name": "Melbourne", "country": "Australia" },
++ { "name": "Cape Town", "country": "South Africa" },
++ { "name": "Rome", "country": "Italy" },
++ { "name": "Vienna", "country": "Austria" },
++ { "name": "Amsterdam", "country": "Netherlands" },
++ { "name": "Prague", "country": "Czech Republic" },
++ { "name": "Lisbon", "country": "Portugal" },
++ { "name": "Budapest", "country": "Hungary" },
++ { "name": "Copenhagen", "country": "Denmark" },
++ { "name": "Stockholm", "country": "Sweden" },
++ { "name": "Oslo", "country": "Norway" },
++ { "name": "Helsinki", "country": "Finland" },
++ { "name": "Dublin", "country": "Ireland" },
++ { "name": "Athens", "country": "Greece" },
++ { "name": "Zurich", "country": "Switzerland" },
++ { "name": "Munich", "country": "Germany" },
++ { "name": "Berlin", "country": "Germany" },
++ { "name": "Brussels", "country": "Belgium" },
++ { "name": "Warsaw", "country": "Poland" },
++ { "name": "Edinburgh", "country": "United Kingdom" },
++ { "name": "Vancouver", "country": "Canada" },
++ { "name": "Montreal", "country": "Canada" },
++ { "name": "San Francisco", "country": "United States" },
++ { "name": "Mexico City", "country": "Mexico" }
++]
ddiff --git a/server/jobs/cities.js b/server/jobs/cities.js
iindex a380668..eaa9e66 100644
---- a/server/jobs/cities.js
++++ b/server/jobs/cities.js
@@@ -16,7 +16,8 @@ const { createWikidata } = require('../sources/wikidata');
const { parseCityPage, pageUrl } = require('../sources/wikivoyage');
const { slugify } = require('../store');
--const CATALOG = path.join(__dirname, '..', '..', 'data', 'top-cities.json');
++// The list of cities to read (committed: data/ is for the development database and is not in git).
++const CATALOG = path.join(__dirname, '..', '..', 'seed', 'top-cities.json');
function loadCatalog(limit = 100) {
const rows = JSON.parse(fs.readFileSync(CATALOG, 'utf8'));
ddiff --git a/test/seed-list.test.js b/test/seed-list.test.js
nnew file mode 100644
iindex 0000000..759ad98
---- /dev/null
++++ b/test/seed-list.test.js
@@@ -0,0 +1,15 @@
++'use strict';
++// The cities job reads its list from seed/top-cities.json, which is committed. It once read data/, which is the
++// development database's folder and not in git, so production started with no cities (10-31).
++const assert = require('assert');
++const fs = require('fs');
++const path = require('path');
++
++const list = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'seed/top-cities.json'), 'utf8'));
++assert.ok(Array.isArray(list) && list.length >= 50, `a real list of cities (${list.length})`);
++for (const c of list) assert.ok(c && typeof c.name === 'string' && c.name && typeof c.country === 'string' && c.country, JSON.stringify(c));
++const job = fs.readFileSync(path.join(__dirname, '..', 'server/jobs/cities.js'), 'utf8');
++assert.match(job, /path\.join\(__dirname, '\.\.', '\.\.', 'seed', 'top-cities\.json'\)/, 'the job reads the committed list');
++const ignore = fs.readFileSync(path.join(__dirname, '..', '.gitignore'), 'utf8');
++assert.ok(!/^seed\/?$/m.test(ignore), 'seed/ is not ignored');
++console.log(`tours seed list: ${list.length} cities, committed and read from seed/`);