// Validation gates for `cargoxx publish` that fail BEFORE any network // I/O. The publish flow performs `nix flake prefetch` + tea API calls, // both of which need live infra. Everything tested here happens earlier // — schema checks of Cargoxx.toml + Cargoxx.lock + git state. #include import cargoxx.cli; import cargoxx.manifest; import cargoxx.lockfile; import cargoxx.util; import std; using cargoxx::cli::cmd_publish; using cargoxx::util::ErrorCode; namespace fs = std::filesystem; namespace manifest = cargoxx::manifest; namespace lockfile = cargoxx::lockfile; namespace { auto fresh_dir() -> fs::path { auto d = fs::temp_directory_path() / std::format("cargoxx-publish-test-{}", std::random_device{}()); fs::create_directories(d); return d; } auto write_manifest(const fs::path& dir, const manifest::Manifest& m) { REQUIRE(manifest::write(m, dir / "Cargoxx.toml").has_value()); } auto write_lock(const fs::path& dir) { lockfile::Lockfile lock{ .version = 1, .packages = {lockfile::LockfilePackage{.name = "foo", .version = "0.1.0"}}, }; REQUIRE(lockfile::write(lock, dir / "Cargoxx.lock").has_value()); } auto minimal_pkg() -> manifest::Package { return manifest::Package{ .name = "foo", .version = "0.1.0", .edition = manifest::Edition::Cpp23, .license = "MIT", }; } } // namespace TEST_CASE("publish rejects a manifest missing [package].license", "[cli][publish]") { auto root = fresh_dir(); auto pkg = minimal_pkg(); pkg.license = std::nullopt; write_manifest(root, manifest::Manifest{pkg, {}, {}}); write_lock(root); auto r = cmd_publish(root, /*dry_run=*/true); REQUIRE_FALSE(r.has_value()); REQUIRE(r.error().code == ErrorCode::ManifestInvalidField); REQUIRE(r.error().message.find("license") != std::string::npos); } TEST_CASE("publish rejects a project without Cargoxx.lock", "[cli][publish]") { auto root = fresh_dir(); write_manifest(root, manifest::Manifest{minimal_pkg(), {}, {}}); // No lockfile. auto r = cmd_publish(root, /*dry_run=*/true); REQUIRE_FALSE(r.has_value()); REQUIRE(r.error().code == ErrorCode::ManifestNotFound); } TEST_CASE("publish rejects path-form dependencies", "[cli][publish]") { auto root = fresh_dir(); auto pkg = minimal_pkg(); manifest::Dependency dep{ .name = "sibling", .version_spec = "*", .source = manifest::DepSource::CargoxxPath, .path = "../sibling", }; write_manifest(root, manifest::Manifest{pkg, {dep}, {}}); write_lock(root); auto r = cmd_publish(root, /*dry_run=*/true); REQUIRE_FALSE(r.has_value()); REQUIRE(r.error().code == ErrorCode::ManifestInvalidField); REQUIRE(r.error().message.find("path dep") != std::string::npos); }