#!/usr/bin/env bash # Isolated, self-cleaning test runner for nigig-core's M-Pesa observation code. # # Why this exists # --------------- # `nigig-core` is commented out of the workspace: it depends on git-hosted # Makepad and Robius crates that are not available in a standalone checkout, so # `cargo test -p nigig-core` cannot run here. That was previously used to argue # the parser/store defects (B1, B4, B6) could not be fixed safely. # # They can. The three files that hold those defects — `parser.rs`, # `classifier.rs` and `store.rs` — depend only on `serde`, `chrono`, one # `log!` macro and one `app_data_dir()` helper. This script copies those files # into a temporary crate, supplies the two shims, and runs their tests under # the toolchain declared in `rust-toolchain.toml`. # # It is a verification harness, not a build target: it proves the pure # parsing/persistence logic is correct without waiting for the whole # application graph to become buildable. # # Usage: # ./tools/test-mpesa-store-clean.sh # KEEP_TEST_ENV=1 ./tools/test-mpesa-store-clean.sh # debugging only set -Eeuo pipefail IFS=$'\n\t' REPO_ROOT="$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" MPESA_DIR="$REPO_ROOT/crates/nigig-core/src/persistence/mpesa" KEEP_TEST_ENV="${KEEP_TEST_ENV:-0}" declared_toolchain() { sed -n 's/^[[:space:]]*channel[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' \ "$REPO_ROOT/rust-toolchain.toml" | head -n 1 } TOOLCHAIN="${RUST_TOOLCHAIN:-$(declared_toolchain)}" if [[ -z "$TOOLCHAIN" ]]; then echo "error: could not read channel from rust-toolchain.toml" >&2 exit 2 fi for required in parser.rs classifier.rs store.rs; do if [[ ! -f "$MPESA_DIR/$required" ]]; then echo "error: expected $MPESA_DIR/$required" >&2 exit 2 fi done WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/nigig-mpesa-test.XXXXXXXX")" cleanup() { local status=$? if [[ "$KEEP_TEST_ENV" == "1" ]]; then echo "test environment retained for debugging: $WORK_DIR" >&2 else rm -rf -- "$WORK_DIR" echo "cleaned isolated M-Pesa store test environment" >&2 fi exit "$status" } trap cleanup EXIT HUP INT TERM export RUSTUP_HOME="$WORK_DIR/rustup" export CARGO_HOME="$WORK_DIR/cargo" export CARGO_TARGET_DIR="$WORK_DIR/target" export PATH="$CARGO_HOME/bin:$PATH" RUSTUP_INIT="$WORK_DIR/rustup-init" echo "creating isolated Rust toolchain ($TOOLCHAIN) in $WORK_DIR" >&2 curl --fail --location --proto '=https' --tlsv1.2 \ https://sh.rustup.rs -o "$RUSTUP_INIT" chmod 700 "$RUSTUP_INIT" "$RUSTUP_INIT" -y --profile minimal --default-toolchain "$TOOLCHAIN" \ --component rustfmt --no-modify-path CRATE="$WORK_DIR/mpesa-store-harness" mkdir -p "$CRATE/src" cat > "$CRATE/Cargo.toml" <<'MANIFEST' [package] name = "mpesa-store-harness" version = "0.1.0" edition = "2021" license = "MIT" publish = false [dependencies] serde = { version = "1", features = ["derive"] } chrono = { version = "0.4", features = ["serde"] } MANIFEST cat > "$CRATE/src/lib.rs" <<'SHIM' //! Test shim for nigig-core's M-Pesa observation modules. //! //! These modules take exactly two things from the wider application: the //! `log!` macro from Makepad, and `app_data_dir()` from nigig-core. Both are //! replaced here so the pure logic can be compiled and tested standalone. macro_rules! log { ($($arg:tt)*) => {{ let _ = format!($($arg)*); }}; } pub(crate) use log; pub mod dir { use std::path::PathBuf; pub fn app_data_dir() -> PathBuf { std::env::temp_dir().join("nigig-mpesa-store-harness") } } pub mod classifier; pub mod parser; pub mod store; SHIM cp "$MPESA_DIR/parser.rs" "$MPESA_DIR/classifier.rs" "$MPESA_DIR/store.rs" \ "$CRATE/src/" # Rewrite the in-crate module paths to the flat harness layout and point the # two external references at the shims above. sed -i \ -e 's#use crate::persistence::mpesa::classifier::#use crate::classifier::#' \ -e 's#use crate::persistence::mpesa::parser::#use crate::parser::#' \ -e 's#use makepad_widgets::log;#use crate::log;#' \ "$CRATE/src/parser.rs" "$CRATE/src/classifier.rs" "$CRATE/src/store.rs" cargo --version rustc --version # Single-threaded: the store writes to one fixed path, so parallel tests would # race on the same file. cargo test --manifest-path "$CRATE/Cargo.toml" -- --test-threads=1 echo "isolated M-Pesa parser/classifier/store tests passed" >&2