Skip to content

Read the ecosystem's own definition of the two test-outcome rules #44

Read the ecosystem's own definition of the two test-outcome rules

Read the ecosystem's own definition of the two test-outcome rules #44

Workflow file for this run

name: Go
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
# Everything that needs no database and no library: the formatting, and that
# every build tag combination compiles and vets. No test runs here, because a
# test collected without the database it reads can only skip, and a skip is a
# test nobody has run reported inside a green job.
build:
name: Build and vet, every tag
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Formatting
run: |
unformatted=$(gofmt -l .)
if [ -n "$unformatted" ]; then
echo "gofmt reports:"; echo "$unformatted"; exit 1
fi
# A file behind a build tag no job ever passes is invisible to every check
# the repository has: it is not compiled, so it is not vetted, and it
# carries no test a count could miss. Compiling each tag set here is what
# keeps backend_duck.go and backend_spark.go from rotting; the meos tag
# needs the library and is compiled in the job below, which builds it.
- name: Vet and build, default tags
run: go vet ./... && go build ./...
- name: Vet and build, duckdb backend
run: go vet -tags duckdb ./... && go build -tags duckdb ./...
- name: Vet and build, spark backend
run: go vet -tags spark ./... && go build -tags spark ./...
# EVERY TEST THIS REPOSITORY CARRIES RUNS HERE, AND NONE OF THEM SKIPS.
# A test skips when a precondition is missing from the job that collects it,
# so this job supplies all three: the fixture database the Annex A data tests
# read, the libmeos the streaming engine links, and the network the published
# OGC schemas are fetched over. It then refuses a skip outright, so a
# precondition that stops being supplied fails the job instead of decaying
# into a green run that exercised nothing.
#
# THE DATABASE IS MOBILITYDB MASTER, BUILT HERE FROM SOURCE. A job pinned to a
# release measures a MobilityDB the project no longer develops, so a defect fixed
# upstream reads here as a live one and blocks work that is not blocked: deleting
# a temporal value that spans a whole composing sequence segfaults the backend on
# 1.3.0 and answers correctly on master. Naming a version somebody can install is
# a reason to ALSO run a release, never a reason to develop against one.
#
# AND A PUBLISHED IMAGE IS NOT THE SOURCE. The mobilitydb/mobilitydb tags are
# built by MobilityDB/MobilityDB-docker, a separate repository on its own schedule,
# so what they carry is that repository's packaging rather than the source this
# tier is written against: `18-3.6-master` fails to load the library on a missing
# libgdal.so.39, and `17-3.5-master` exits its own entrypoint because the
# pointcloud extension it asks for is not available. Pinning whichever tag happens
# to work makes the packaging the source of truth and dates the measurement to
# whenever that tag was pushed. The recipe below is MobilityDB's own
# .github/workflows/pgversion.yml, read from the checkout under test.
conformance:
name: Every test, against MobilityDB master
runs-on: ubuntu-24.04
env:
PGVERSION: "18"
POSTGISVERSION: "3"
MFAPI_DSN: postgres://postgres:conformance@127.0.0.1:5432/mfapi?sslmode=disable
# The vendored OGC schema bundle is compared against what OGC publishes.
MFAPI_SCHEMA_FRESHNESS: "1"
# The floor is every result line `go test -v` writes, subtests included:
# 77 top-level tests and 64 subtests. Subtests belong in it because a
# t.Run whose body skips is as unrun as its parent would be, and deleting
# a case from a table-driven test moves nothing else. Raise it when the
# suite grows. Lowering it belongs in the same commit as the removal it
# accounts for, because deleting a test is what a skip refusal cannot see.
MFAPI_TEST_FLOOR: "141"
steps:
- uses: actions/checkout@v4
- name: Check out MobilityDB master
uses: actions/checkout@v4
with:
repository: MobilityDB/MobilityDB
ref: master
path: mobilitydb
- name: Name the commit under test
run: git -C mobilitydb log -1 --format='MobilityDB master %H %s'
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Remove the runner's PostgreSQL, so apt.postgresql.org answers
run: |
sudo service postgresql stop || true
sudo apt-get --purge remove postgresql* -y || true
sudo rm -rf /var/lib/postgresql/ /etc/postgresql/ /var/log/postgresql/ || true
# MobilityDB's own apt hardening, taken from the checkout above rather than
# copied: the runner image ships two package sources that answer 403 from the
# Azure runners, which fails an apt-get update asking only for Ubuntu packages.
- uses: ./mobilitydb/.github/actions/apt-resilient
- name: Add the PostgreSQL APT repository
run: |
sudo apt-get install -y curl ca-certificates gnupg
curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
codename=$(lsb_release -cs)
echo "deb http://apt.postgresql.org/pub/repos/apt/ ${codename}-pgdg main ${PGVERSION}" \
| sudo tee /etc/apt/sources.list.d/pgdg.list
- name: Install the build and server dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
libgeos++-dev \
libproj-dev \
libgdal-dev \
libjson-c-dev \
postgresql-${PGVERSION} \
postgresql-${PGVERSION}-postgis-${POSTGISVERSION} \
postgresql-server-dev-${PGVERSION}
# The default family set is what this tier reads: temporal geometry points
# and the scalar temporal types. The optional families carry dependencies of
# their own and no route here reaches one, so the build stays at the defaults
# and the job measures the surface it uses.
- name: Build and install MobilityDB
run: |
export PATH=/usr/lib/postgresql/${PGVERSION}/bin:$PATH
mkdir mobilitydb/build
cd mobilitydb/build
cmake -DCMAKE_BUILD_TYPE=Release ..
make -j "$(nproc)"
sudo make install
# The same source, configured as the standalone library the streaming
# engine links. Building it from the checkout above is what makes the
# engine tests measure the same MobilityDB the SQL routes measure; a
# libmeos from anywhere else would be a second, unnamed commit.
- name: Build and install libmeos from the same checkout
run: |
mkdir mobilitydb/build-meos
cd mobilitydb/build-meos
cmake -DMEOS=ON -DCMAKE_BUILD_TYPE=Release ..
make -j "$(nproc)"
sudo make install
sudo ldconfig
# LOADING THE EXTENSION IS THE ASSERTION THE PUBLISHED IMAGES FAILED. Doing
# it in a step of its own, ahead of the fixture, is what makes a database that
# cannot carry MobilityDB say so here rather than inside a fixture load.
- name: Create the database and load the extension
run: |
sudo service postgresql start
sudo -u postgres psql -v ON_ERROR_STOP=1 \
-c "ALTER USER postgres PASSWORD 'conformance'"
sudo -u postgres createdb mfapi
psql "$MFAPI_DSN" -v ON_ERROR_STOP=1 -q \
-c "CREATE EXTENSION mobilitydb CASCADE"
psql "$MFAPI_DSN" -X -A -t -c "SELECT mobilitydbFullVersion()"
- name: Load the conformance fixture
run: psql "$MFAPI_DSN" -v ON_ERROR_STOP=1 -q -f tutorial/setup/load_conformance.sql
# THE EXIT STATUS OF A PIPELINE IS ITS LAST STAGE'S. Without pipefail the
# status read here is tee's, which is always 0, and a failing test reaches the
# guard below as though it had passed.
- name: The whole suite, with every precondition supplied
run: |
set -o pipefail
go test -tags meos -v ./... 2>&1 | tee suite.log
# A SKIP IS NOT A PASS, AND A DELETION IS NOT VISIBLE IN THE SKIP COUNT.
# Two rules, because each is blind to what the other catches. The first
# refuses a test that reported itself unrun; once that stands, deleting a
# test is the remaining way to stop running it, and it leaves no skip
# behind, so only the total moves.
#
# The rules live once, in MEOS-API beside the catalog, and every consumer
# in the ecosystem reads that one definition rather than a copy of it: a
# copy is what drifts, and a rule about coverage that drifts is worse than
# none. The same script answers over a local log, so a by-hand answer and
# the CI answer cannot differ.
- name: Refuse a skip, and a suite that shrank
uses: MobilityDB/MEOS-API/.github/actions/check-test-outcome@master
with:
log: suite.log
min-tests: ${{ env.MFAPI_TEST_FLOOR }}
# A SAMPLE THAT HAS DRIFTED IS WORSE THAN NO SAMPLE: it states what the
# service used to answer, and a reader has no way to tell. Re-emitting here
# and diffing against what is committed is what keeps samples/ the current
# answer rather than a snapshot somebody forgot to refresh.
#
# The TemporalProperties document carries the time it was written, which the
# standard defines it to carry, so that one line is normalised on both sides.
- name: The committed samples are what the service writes
run: |
set -o pipefail
go run . -emit samples.emitted
norm() { sed -E 's/"timeStamp": "[^"]*"/"timeStamp": "<written>"/' "$1"; }
rc=0
for f in samples/*; do
b=$(basename "$f")
if [ ! -f "samples.emitted/$b" ]; then
echo "::error::samples/$b is no longer a resource the service serves"; rc=1; continue
fi
if ! diff -q <(norm "$f") <(norm "samples.emitted/$b") >/dev/null; then
echo "::error::samples/$b is not what the service writes; re-run: go run . -emit samples"
diff -u <(norm "$f") <(norm "samples.emitted/$b") | head -40
rc=1
fi
done
for f in samples.emitted/*; do
b=$(basename "$f")
if [ ! -f "samples/$b" ]; then
echo "::error::the service serves a resource samples/ does not carry: $b"; rc=1
fi
done
if [ "$rc" -eq 0 ]; then
echo "$(ls samples/*.json | wc -l) sample documents are what the service writes"
fi
exit $rc