#![allow(dead_code)]
use assert_cmd::Command;
use provenance_core::SUPPORTED_SCHEMA_VERSION;
use serde_json::{json, Value};
pub fn provenance() -> Command {
Command::new(assert_cmd::cargo::cargo_bin!("provenance"))
}
pub fn init_repo() -> tempfile::TempDir {
let directory = tempfile::tempdir().unwrap();
provenance()
.args([
"init",
"--path",
directory.path().to_str().unwrap(),
"--scope",
"default",
"--path-prefix",
".",
])
.assert()
.success();
directory
}
pub fn sdk(repo: &str, command: &str, input: &Value) -> Value {
let output = provenance()
.args([
"sdk", command, "--repo", repo, "--scope", "default", "--format", "json",
])
.write_stdin(serde_json::to_vec(input).unwrap())
.output()
.unwrap();
assert!(
output.status.success(),
"sdk {command} failed: {}",
String::from_utf8_lossy(&output.stderr)
);
serde_json::from_slice(&output.stdout).unwrap()
}
pub fn sdk_raw(repo: &str, command: &str, input: &Value) -> (bool, String, String) {
let output = provenance()
.args([
"sdk", command, "--repo", repo, "--scope", "default", "--format", "json",
])
.write_stdin(serde_json::to_vec(input).unwrap())
.output()
.unwrap();
(
output.status.success(),
String::from_utf8_lossy(&output.stdout).into_owned(),
String::from_utf8_lossy(&output.stderr).into_owned(),
)
}
pub fn sdk_error(repo: &str, command: &str, input: &Value) -> String {
let (success, stdout, stderr) = sdk_raw(repo, command, input);
assert!(!success, "sdk {command} unexpectedly succeeded: {stdout}");
stderr
}
pub fn shared_rule_spec() -> Value {
json!({
"schema_version": SUPPORTED_SCHEMA_VERSION.0,
"spec": "share-links",
"declared_by": "spec://typescript/share-links",
"sources": [{
"key": "retention",
"name": "Retention policy",
"kind": "policy",
"reference": "docs/retention.md"
}],
"requirements": [
{
"key": "sharing",
"statement": "Shares are time bounded",
"sources": ["retention"]
},
{
"key": "sessions",
"statement": "Sessions are time bounded",
"sources": ["retention"]
}
],
"rules": [{
"key": "expiry",
"requirements": ["sharing", "sessions"],
"statement": "Share links expire within 30 days",
"implementation": {
"file": "share-links.ts",
"symbol": "createShareLink"
}
}]
})
}
pub fn apply_shared_rule(directory: &tempfile::TempDir) -> Ids {
let repo = directory.path().to_str().unwrap();
std::fs::write(
directory.path().join("share-links.ts"),
"export function createShareLink() {}\n",
)
.unwrap();
let result = sdk(repo, "apply", &shared_rule_spec());
Ids {
source: resource_id(&result, "source", "retention"),
sharing: resource_id(&result, "requirement", "sharing"),
sessions: resource_id(&result, "requirement", "sessions"),
rule: resource_id(&result, "rule", "expiry"),
}
}
pub fn verify_rule(repo: &str, rule: &str) -> Value {
std::fs::write(
std::path::Path::new(repo).join("share-links.test.ts"),
"// verification fixture\n",
)
.unwrap();
let run = sdk(
repo,
"begin-verification",
&json!({
"rule": rule,
"key": "share-link-expiry",
"method": "examples",
"declared_by": "ci://typescript",
"file": "share-links.test.ts",
"symbol": "expiry test"
}),
);
sdk(
repo,
"complete-verification",
&json!({"run": run["id"].as_str().unwrap(), "status": "passed"}),
)
}
pub fn restate_sharing(repo: &str) -> Value {
let mut restated = shared_rule_spec();
restated["requirements"][0]["statement"] = json!("Shares are time bounded and revocable");
sdk(repo, "apply", &restated)
}
pub struct Ids {
pub source: String,
pub sharing: String,
pub sessions: String,
pub rule: String,
}
pub fn resource_id(result: &Value, kind: &str, key: &str) -> String {
result["resources"]
.as_array()
.unwrap()
.iter()
.find(|resource| resource["kind"] == kind && resource["key"] == key)
.unwrap_or_else(|| panic!("no {kind} resource keyed {key} in {result}"))["id"]
.as_str()
.unwrap()
.to_string()
}
pub fn ids_of(nodes: &Value) -> Vec<String> {
nodes
.as_array()
.unwrap()
.iter()
.map(|node| node["id"].as_str().unwrap().to_string())
.collect()
}