Files
clawmates/tools/bundler/tests/bundle.rs
T
Omar SobhandClaude Fable 5 ace66d7ffb P6 complete: PWA, route motion, dex browser-flow OAuth, release pipeline
- PWA (§16): hand-rolled 60-line service worker (network-first pages with
  offline fallback, cache-first hashed statics, /api NEVER touched — SSE
  and approvals stay live), app manifest with §2 identity, stdlib-
  generated coral claw icons, prod-only registration. E2E asserts
  manifest, real PNG icons, an ACTIVATED service worker, and the /api
  bypass. (Serwist was tried and dropped: its webpack plugin fights
  Next 16's Turbopack builds; sixty lines we own beat a plugin we fight.)
- Route motion (§3): (workspace) template re-mounts per navigation with a
  quiet fade-rise, zeroed under prefers-reduced-motion. The a11y sweep
  now settles running animations before scanning — axe was reading
  mid-fade opacity as contrast failures
- OAuth browser flow vs REAL dex: the e2e harness boots dexidp/dex with
  static client + password; the journey drives the actual dex login form
  from /api/apps/oauth/start through the callback 303 and asserts the
  app reads connected (closing the P4 deferral honestly)
- release.yml: tag-triggered — builds all four images + postgres, saves
  tarballs, assembles the SIGNED air-gapped bundle (compose, config,
  migrations, seccomp profile, installer, bundler binary), derives the
  public key via the new Could not find command "pubkey". subcommand (tested), verifies
  the bundle customer-style with the public half only, attaches tarball
  + public key to the GitHub release

153 Rust + 63 frontend tests + 29 Playwright journeys.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-10 09:57:21 -05:00

114 lines
3.6 KiB
Rust

use std::fs;
use std::path::PathBuf;
use teamclaw_bundler::{assemble, generate_keypair, verify, BundleError};
struct Fixture {
root: PathBuf,
public_key: PathBuf,
bundle: PathBuf,
}
fn fixture() -> Fixture {
let root = std::env::temp_dir().join(format!("tc-bundle-{}", uuid::Uuid::now_v7()));
fs::create_dir_all(&root).unwrap();
let private_key = root.join("release.key");
let public_key = root.join("release.pub");
generate_keypair(&private_key, &public_key).unwrap();
fs::write(root.join("server.tar"), b"pretend image bytes").unwrap();
fs::write(root.join("compose.yml"), b"services: {}").unwrap();
fs::write(root.join("0001_init.sql"), b"CREATE TABLE t ();").unwrap();
let bundle = root.join("bundle");
assemble(
&bundle,
"1.2.3",
&[
(root.join("server.tar"), "images/server.tar".into()),
(
root.join("compose.yml"),
"compose/docker-compose.yml".into(),
),
(
root.join("0001_init.sql"),
"migrations/0001_init.sql".into(),
),
],
&private_key,
)
.unwrap();
Fixture {
root,
public_key,
bundle,
}
}
#[test]
fn assembled_bundles_verify_offline() {
let fx = fixture();
// 3 artifacts + manifest.json.
assert_eq!(verify(&fx.bundle, &fx.public_key).unwrap(), 4);
let manifest: serde_json::Value =
serde_json::from_str(&fs::read_to_string(fx.bundle.join("manifest.json")).unwrap())
.unwrap();
assert_eq!(manifest["version"], "1.2.3");
assert_eq!(manifest["artifacts"].as_array().unwrap().len(), 3);
}
#[test]
fn a_tampered_artifact_is_detected() {
let fx = fixture();
fs::write(fx.bundle.join("images/server.tar"), b"EVIL image bytes").unwrap();
let err = verify(&fx.bundle, &fx.public_key).unwrap_err();
assert!(matches!(err, BundleError::ChecksumMismatch(p) if p == "images/server.tar"));
}
#[test]
fn a_tampered_checksum_list_fails_the_signature() {
let fx = fixture();
// Attacker edits both the artifact AND its checksum line — the
// signature over checksums.txt catches it.
let listing = fs::read_to_string(fx.bundle.join("checksums.txt")).unwrap();
fs::write(
fx.bundle.join("checksums.txt"),
listing.replace("images/server.tar", "images/sneaky.tar"),
)
.unwrap();
let err = verify(&fx.bundle, &fx.public_key).unwrap_err();
assert!(matches!(err, BundleError::BadSignature));
}
#[test]
fn the_wrong_public_key_is_refused() {
let fx = fixture();
let other_private = fx.root.join("other.key");
let other_public = fx.root.join("other.pub");
generate_keypair(&other_private, &other_public).unwrap();
let err = verify(&fx.bundle, &other_public).unwrap_err();
assert!(matches!(err, BundleError::BadSignature));
}
#[test]
fn a_missing_artifact_is_reported() {
let fx = fixture();
fs::remove_file(fx.bundle.join("migrations/0001_init.sql")).unwrap();
let err = verify(&fx.bundle, &fx.public_key).unwrap_err();
assert!(matches!(err, BundleError::Missing(p) if p.contains("0001_init.sql")));
}
#[test]
fn the_public_key_rederives_from_the_signing_key() {
let fx = fixture();
let derived = fx.root.join("derived.pub");
teamclaw_bundler::derive_public_key(&fx.root.join("release.key"), &derived).unwrap();
assert_eq!(
fs::read_to_string(&derived).unwrap(),
fs::read_to_string(&fx.public_key).unwrap()
);
// And it verifies the bundle, same as the original.
assert_eq!(verify(&fx.bundle, &derived).unwrap(), 4);
}