use chrono::{DateTime, NaiveDate, Utc};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
use uuid::Uuid;
pub const SCHEMA_VERSION: &str = "perf-sentinel-report/v1.3";
pub const SCOPE_OPERATOR_DECLARED_FIELDS: &[&str] = &[
"total_applications_declared",
"total_requests_in_period",
"applications_excluded",
"environments_measured",
"environments_excluded",
];
pub const SCOPE_MACHINE_DERIVED_FIELDS: &[&str] = &[
"applications_measured",
"requests_measured",
"coverage_percentage",
];
pub const CORE_PATTERNS_REQUIRED: &[&str] = &[
"n_plus_one_sql",
"n_plus_one_http",
"redundant_sql",
"redundant_http",
];
#[must_use]
pub fn core_patterns_required() -> Vec<String> {
CORE_PATTERNS_REQUIRED
.iter()
.map(|s| (*s).to_string())
.collect()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PeriodicReport {
pub schema_version: String,
pub report_metadata: ReportMetadata,
pub organisation: Organisation,
pub period: Period,
pub scope_manifest: ScopeManifest,
pub methodology: Methodology,
pub aggregate: Aggregate,
pub applications: Vec<Application>,
pub integrity: Integrity,
pub notes: Notes,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ReportIntent {
Internal,
Official,
Audited,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Confidentiality {
Internal,
Public,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum IntegrityLevel {
None,
HashOnly,
Signed,
SignedWithAttestation,
Audited,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum PeriodType {
CalendarQuarter,
CalendarMonth,
CalendarYear,
Custom,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Conformance {
CoreRequired,
Extended,
Partial,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReportMetadata {
pub intent: ReportIntent,
pub confidentiality_level: Confidentiality,
pub integrity_level: IntegrityLevel,
pub generated_at: DateTime<Utc>,
pub generated_by: String,
pub perf_sentinel_version: String,
pub report_uuid: Uuid,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub binary_version: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Organisation {
pub name: String,
pub country: String,
#[serde(default)]
pub identifiers: OrgIdentifiers,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sector: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct OrgIdentifiers {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub siren: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub vat: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub lei: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub opencorporates_url: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub domain: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Period {
pub from_date: NaiveDate,
pub to_date: NaiveDate,
pub period_type: PeriodType,
pub days_covered: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScopeManifest {
pub total_applications_declared: u32,
pub applications_measured: u32,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub applications_excluded: Vec<ExcludedApp>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub environments_measured: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub environments_excluded: Vec<ExcludedEnv>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total_requests_in_period: Option<u64>,
pub requests_measured: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub coverage_percentage: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub coverage_basis: Option<CoverageBasis>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CoverageBasis {
pub operator_declared: Vec<String>,
pub machine_derived: Vec<String>,
}
impl CoverageBasis {
#[must_use]
pub fn standard() -> Self {
Self {
operator_declared: SCOPE_OPERATOR_DECLARED_FIELDS
.iter()
.map(|s| (*s).to_string())
.collect(),
machine_derived: SCOPE_MACHINE_DERIVED_FIELDS
.iter()
.map(|s| (*s).to_string())
.collect(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StandardCrosswalk {
pub standard: String,
pub mappings: Vec<CrosswalkEntry>,
pub caveats: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CrosswalkEntry {
pub report_field: String,
pub datapoint: String,
pub note: String,
}
impl StandardCrosswalk {
#[must_use]
pub fn esrs_e1() -> Self {
Self {
standard: "ESRS E1 (Delegated Regulation (EU) 2023/5303)".to_string(),
mappings: vec![
CrosswalkEntry {
report_field: "aggregate.total_energy_kwh".to_string(),
datapoint: "E1-5 Energy consumption and mix".to_string(),
note: "Convert kWh to MWh. Not disaggregated by fossil, nuclear or renewable source.".to_string(),
},
CrosswalkEntry {
report_field: "aggregate.total_carbon_kgco2eq (operational term)".to_string(),
datapoint: "E1-6 Gross Scope 2 GHG emissions (location-based)".to_string(),
note: "Location-based only. ESRS also requires a market-based figure, which SCI excludes.".to_string(),
},
CrosswalkEntry {
report_field: "embodied carbon (SCI M term, aggregate only)".to_string(),
datapoint: "E1-6 Gross Scope 3 GHG emissions (categories 1 and 2)".to_string(),
note: "Hardware lifecycle. ESRS admits estimates and proxy data for Scope 3.".to_string(),
},
],
caveats: vec![
"Interpretive crosswalk, not a certification or an audited inventory.".to_string(),
"Figures carry a 2x directional uncertainty bracket.".to_string(),
"Scope is IT compute only. It covers neither non-IT scopes nor the market-based Scope 2 figure.".to_string(),
],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExcludedApp {
pub service_name: String,
pub reason: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExcludedEnv {
pub name: String,
pub reason: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Methodology {
pub sci_specification: String,
pub perf_sentinel_version: String,
pub enabled_patterns: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub disabled_patterns: Vec<DisabledPattern>,
pub core_patterns_required: Vec<String>,
pub conformance: Conformance,
pub calibration_inputs: CalibrationInputs,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub standard_crosswalk: Option<StandardCrosswalk>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DisabledPattern {
pub name: String,
pub reason: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CalibrationInputs {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub cloud_regions: Vec<String>,
pub carbon_intensity_source: String,
pub specpower_table_version: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub binary_specpower_vintage: Option<String>,
pub scaphandre_used: bool,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub energy_source_models: BTreeSet<String>,
#[serde(default)]
pub calibration_applied: bool,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct WasteTier {
pub n_plus_one_threshold: u32,
pub energy_kwh: f64,
pub carbon_kgco2eq: f64,
pub waste_ratio: f64,
pub efficiency_score: f64,
}
impl WasteTier {
#[must_use]
pub fn is_default(&self) -> bool {
*self == Self::default()
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct TemporalCoverage {
pub temporal_coverage: f64,
pub observed_days: u32,
pub days_in_period: u32,
pub largest_gap_days: u32,
}
impl TemporalCoverage {
#[must_use]
pub fn is_default(&self) -> bool {
*self == Self::default()
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Aggregate {
pub total_requests: u64,
pub total_energy_kwh: f64,
pub total_carbon_kgco2eq: f64,
pub aggregate_efficiency_score: f64,
pub aggregate_waste_ratio: f64,
pub anti_patterns_detected_count: u64,
pub estimated_optimization_potential_kgco2eq: f64,
#[serde(default, skip_serializing_if = "WasteTier::is_default")]
pub canonical_waste: WasteTier,
#[serde(default, skip_serializing_if = "WasteTier::is_default")]
pub operational_waste: WasteTier,
#[serde(default = "default_period_coverage")]
pub period_coverage: f64,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub binary_versions: BTreeSet<String>,
#[serde(default)]
pub runtime_windows_count: u64,
#[serde(default)]
pub fallback_windows_count: u64,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub per_service_energy_models: BTreeMap<String, BTreeSet<String>>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub per_service_measured_ratio: BTreeMap<String, f64>,
#[serde(default, skip_serializing_if = "TemporalCoverage::is_default")]
pub temporal_coverage: TemporalCoverage,
}
#[inline]
const fn default_period_coverage() -> f64 {
1.0
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Application {
G1(ApplicationG1),
G2(ApplicationG2),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApplicationG1 {
pub service_name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub display_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub service_version: Option<String>,
pub endpoints_observed: u32,
pub total_requests: u64,
pub energy_kwh: f64,
pub carbon_kgco2eq: f64,
pub efficiency_score: f64,
pub anti_patterns: Vec<AntiPatternDetail>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApplicationG2 {
pub service_name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub display_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub service_version: Option<String>,
pub endpoints_observed: u32,
pub total_requests: u64,
pub energy_kwh: f64,
pub carbon_kgco2eq: f64,
pub efficiency_score: f64,
pub anti_patterns_detected_count: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AntiPatternDetail {
#[serde(rename = "type")]
pub kind: String,
pub occurrences: u64,
pub estimated_waste_kwh: f64,
pub estimated_waste_kgco2eq: f64,
pub first_seen: DateTime<Utc>,
pub last_seen: DateTime<Utc>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub rgesn_criteria: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Integrity {
pub content_hash: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub binary_hash: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub binary_verification_url: Option<String>,
#[serde(default)]
pub trace_integrity_chain: serde_json::Value,
#[serde(default)]
pub signature: Option<SignatureMetadata>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub binary_attestation: Option<BinaryAttestationMetadata>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cross_period_log: Option<CrossPeriodLogRef>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CrossPeriodLogRef {
pub format: String,
pub log_url: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub previous_report_hash: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SignatureMetadata {
pub format: String,
pub bundle_url: String,
pub signer_identity: String,
pub signer_issuer: String,
pub rekor_url: String,
pub rekor_log_index: u64,
pub signed_at: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BinaryAttestationMetadata {
pub format: String,
pub attestation_url: String,
pub builder_id: String,
pub git_tag: String,
pub git_commit: String,
pub slsa_level: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Notes {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub disclaimers: Vec<String>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub reference_urls: BTreeMap<String, String>,
}
#[cfg(test)]
mod tests {
use super::*;
use core::assert_matches;
fn sample_metadata() -> ReportMetadata {
ReportMetadata {
intent: ReportIntent::Internal,
confidentiality_level: Confidentiality::Internal,
integrity_level: IntegrityLevel::HashOnly,
generated_at: DateTime::parse_from_rfc3339("2026-04-01T00:00:00Z")
.unwrap()
.with_timezone(&Utc),
generated_by: "cli-batch".to_string(),
perf_sentinel_version: "0.6.2".to_string(),
report_uuid: Uuid::nil(),
binary_version: String::new(),
}
}
fn sample_organisation() -> Organisation {
Organisation {
name: "Acme Corp".to_string(),
country: "FR".to_string(),
identifiers: OrgIdentifiers {
siren: Some("123456789".to_string()),
..Default::default()
},
sector: Some("62.01".to_string()),
}
}
fn sample_period() -> Period {
Period {
from_date: NaiveDate::from_ymd_opt(2026, 1, 1).unwrap(),
to_date: NaiveDate::from_ymd_opt(2026, 3, 31).unwrap(),
period_type: PeriodType::CalendarQuarter,
days_covered: 90,
}
}
fn sample_scope() -> ScopeManifest {
ScopeManifest {
total_applications_declared: 5,
applications_measured: 4,
applications_excluded: vec![ExcludedApp {
service_name: "legacy-batch".to_string(),
reason: "instrumentation pending".to_string(),
}],
environments_measured: vec!["prod".to_string()],
environments_excluded: vec![],
total_requests_in_period: Some(1_000_000),
requests_measured: 980_000,
coverage_percentage: Some(98.0),
coverage_basis: Some(CoverageBasis::standard()),
}
}
fn sample_methodology() -> Methodology {
Methodology {
sci_specification: "ISO/IEC 21031:2024".to_string(),
perf_sentinel_version: "0.6.2".to_string(),
enabled_patterns: vec!["n_plus_one_sql".to_string(), "slow_sql".to_string()],
disabled_patterns: vec![],
core_patterns_required: core_patterns_required(),
conformance: Conformance::CoreRequired,
calibration_inputs: super::super::test_fixtures::sample_calibration_inputs(),
standard_crosswalk: None,
}
}
fn sample_aggregate() -> Aggregate {
Aggregate {
total_requests: 980_000,
total_energy_kwh: 12.5,
total_carbon_kgco2eq: 1.4,
aggregate_efficiency_score: 82.0,
aggregate_waste_ratio: 0.18,
anti_patterns_detected_count: 47,
estimated_optimization_potential_kgco2eq: 0.25,
canonical_waste: WasteTier {
n_plus_one_threshold: 2,
energy_kwh: 2.1,
carbon_kgco2eq: 0.25,
waste_ratio: 0.18,
efficiency_score: 82.0,
},
operational_waste: WasteTier {
n_plus_one_threshold: 5,
energy_kwh: 0.9,
carbon_kgco2eq: 0.12,
waste_ratio: 0.08,
efficiency_score: 92.0,
},
period_coverage: 1.0,
binary_versions: BTreeSet::new(),
runtime_windows_count: 0,
fallback_windows_count: 0,
per_service_energy_models: BTreeMap::new(),
per_service_measured_ratio: BTreeMap::new(),
temporal_coverage: TemporalCoverage {
temporal_coverage: 0.9,
observed_days: 81,
days_in_period: 90,
largest_gap_days: 3,
},
}
}
fn sample_integrity() -> Integrity {
Integrity {
content_hash: "sha256:".to_string()
+ "0000000000000000000000000000000000000000000000000000000000000000",
binary_hash: None,
binary_verification_url: None,
trace_integrity_chain: serde_json::Value::Null,
signature: None,
binary_attestation: None,
cross_period_log: None,
}
}
fn sample_notes() -> Notes {
let mut urls = BTreeMap::new();
urls.insert(
"project".to_string(),
"https://github.com/robintra/perf-sentinel".to_string(),
);
Notes {
disclaimers: vec!["Directional estimate, not regulatory-grade".to_string()],
reference_urls: urls,
}
}
fn sample_g1_app() -> ApplicationG1 {
ApplicationG1 {
service_name: "checkout".to_string(),
display_name: Some("Checkout".to_string()),
service_version: Some("v1.4.2".to_string()),
endpoints_observed: 12,
total_requests: 240_000,
energy_kwh: 4.1,
carbon_kgco2eq: 0.46,
efficiency_score: 78.0,
anti_patterns: vec![AntiPatternDetail {
kind: "n_plus_one_sql".to_string(),
occurrences: 12,
estimated_waste_kwh: 0.05,
estimated_waste_kgco2eq: 0.006,
first_seen: DateTime::parse_from_rfc3339("2026-01-04T10:00:00Z")
.unwrap()
.with_timezone(&Utc),
last_seen: DateTime::parse_from_rfc3339("2026-03-29T18:00:00Z")
.unwrap()
.with_timezone(&Utc),
rgesn_criteria: Vec::new(),
}],
}
}
fn sample_g2_app() -> ApplicationG2 {
ApplicationG2 {
service_name: "checkout".to_string(),
display_name: Some("Checkout".to_string()),
service_version: None,
endpoints_observed: 12,
total_requests: 240_000,
energy_kwh: 4.1,
carbon_kgco2eq: 0.46,
efficiency_score: 78.0,
anti_patterns_detected_count: 12,
}
}
fn sample_report(applications: Vec<Application>) -> PeriodicReport {
PeriodicReport {
schema_version: SCHEMA_VERSION.to_string(),
report_metadata: sample_metadata(),
organisation: sample_organisation(),
period: sample_period(),
scope_manifest: sample_scope(),
methodology: sample_methodology(),
aggregate: sample_aggregate(),
applications,
integrity: sample_integrity(),
notes: sample_notes(),
}
}
#[test]
fn roundtrip_v1_minimal() {
let r = sample_report(vec![]);
let json = serde_json::to_string(&r).unwrap();
let back: PeriodicReport = serde_json::from_str(&json).unwrap();
let json2 = serde_json::to_string(&back).unwrap();
assert_eq!(json, json2);
assert_eq!(back.schema_version, SCHEMA_VERSION);
assert!(back.applications.is_empty());
}
#[test]
fn default_waste_tiers_omitted_from_serialization() {
let mut agg = sample_aggregate();
assert!(
serde_json::to_value(&agg)
.unwrap()
.get("canonical_waste")
.is_some()
);
agg.canonical_waste = WasteTier::default();
agg.operational_waste = WasteTier::default();
let json = serde_json::to_value(&agg).unwrap();
assert!(json.get("canonical_waste").is_none());
assert!(json.get("operational_waste").is_none());
}
#[test]
fn roundtrip_v1_full_g1() {
let r = sample_report(vec![Application::G1(sample_g1_app())]);
let json = serde_json::to_string(&r).unwrap();
let back: PeriodicReport = serde_json::from_str(&json).unwrap();
assert_matches!(back.applications[0], Application::G1(_));
let Application::G1(ref app) = back.applications[0] else {
unreachable!()
};
assert_eq!(app.anti_patterns.len(), 1);
assert_eq!(app.anti_patterns[0].kind, "n_plus_one_sql");
}
#[test]
fn roundtrip_v1_full_g2() {
let r = sample_report(vec![Application::G2(sample_g2_app())]);
let json = serde_json::to_string(&r).unwrap();
let back: PeriodicReport = serde_json::from_str(&json).unwrap();
assert_matches!(back.applications[0], Application::G2(_));
let Application::G2(ref app) = back.applications[0] else {
unreachable!()
};
assert_eq!(app.anti_patterns_detected_count, 12);
}
#[test]
fn application_g1_disambiguates_from_g2() {
let g1 = serde_json::json!({
"service_name": "svc",
"endpoints_observed": 1,
"total_requests": 10,
"energy_kwh": 0.1,
"carbon_kgco2eq": 0.01,
"efficiency_score": 90.0,
"anti_patterns": []
});
let g2 = serde_json::json!({
"service_name": "svc",
"endpoints_observed": 1,
"total_requests": 10,
"energy_kwh": 0.1,
"carbon_kgco2eq": 0.01,
"efficiency_score": 90.0,
"anti_patterns_detected_count": 0
});
assert_matches!(
serde_json::from_value::<Application>(g1).unwrap(),
Application::G1(_)
);
assert_matches!(
serde_json::from_value::<Application>(g2).unwrap(),
Application::G2(_)
);
}
#[test]
fn core_patterns_required_matches_constant() {
let v = core_patterns_required();
assert_eq!(v.len(), CORE_PATTERNS_REQUIRED.len());
assert!(v.contains(&"n_plus_one_sql".to_string()));
assert!(v.contains(&"n_plus_one_http".to_string()));
assert!(v.contains(&"redundant_sql".to_string()));
assert!(v.contains(&"redundant_http".to_string()));
}
#[test]
fn enum_serialization_uses_kebab_or_snake() {
let v = serde_json::to_string(&PeriodType::CalendarQuarter).unwrap();
assert_eq!(v, "\"calendar-quarter\"");
let v = serde_json::to_string(&IntegrityLevel::HashOnly).unwrap();
assert_eq!(v, "\"hash-only\"");
let v = serde_json::to_string(&ReportIntent::Official).unwrap();
assert_eq!(v, "\"official\"");
}
#[test]
fn unknown_top_level_fields_tolerated() {
let mut v = serde_json::to_value(sample_report(vec![])).unwrap();
v.as_object_mut()
.unwrap()
.insert("future_field".to_string(), serde_json::json!("ignore me"));
let _: PeriodicReport = serde_json::from_value(v).unwrap();
}
#[test]
fn aggregate_period_coverage_defaults_to_one_when_missing() {
let legacy = serde_json::json!({
"total_requests": 0,
"total_energy_kwh": 0.0,
"total_carbon_kgco2eq": 0.0,
"aggregate_efficiency_score": 100.0,
"aggregate_waste_ratio": 0.0,
"anti_patterns_detected_count": 0,
"estimated_optimization_potential_kgco2eq": 0.0
});
let agg: Aggregate = serde_json::from_value(legacy).unwrap();
assert!((agg.period_coverage - 1.0).abs() < f64::EPSILON);
assert_eq!(agg.runtime_windows_count, 0);
assert_eq!(agg.fallback_windows_count, 0);
assert!(agg.binary_versions.is_empty());
}
fn sample_signature() -> SignatureMetadata {
SignatureMetadata {
format: "sigstore-cosign-intoto-v1".to_string(),
bundle_url: "https://example.fr/perf-sentinel-attestation.sig".to_string(),
signer_identity: "https://github.com/robintra/perf-sentinel/.github/workflows/release.yml@refs/tags/v0.7.0".to_string(),
signer_issuer: "https://token.actions.githubusercontent.com".to_string(),
rekor_url: "https://rekor.sigstore.dev".to_string(),
rekor_log_index: 123_456_789,
signed_at: "2026-05-14T16:00:00Z".to_string(),
}
}
fn sample_attestation() -> BinaryAttestationMetadata {
BinaryAttestationMetadata {
format: "slsa-provenance-v1".to_string(),
attestation_url: "https://github.com/robintra/perf-sentinel/releases/download/v0.7.0/perf-sentinel-linux-amd64.intoto.jsonl".to_string(),
builder_id: "https://github.com/actions/runner".to_string(),
git_tag: "v0.7.0".to_string(),
git_commit: "a47be9d".to_string(),
slsa_level: "L2".to_string(),
}
}
#[test]
fn integrity_roundtrip_with_signature_and_attestation() {
let i = Integrity {
content_hash: "sha256:".to_string() + &"0".repeat(64),
binary_hash: None,
binary_verification_url: None,
trace_integrity_chain: serde_json::Value::Null,
signature: Some(sample_signature()),
binary_attestation: Some(sample_attestation()),
cross_period_log: None,
};
let s = serde_json::to_string(&i).unwrap();
let back: Integrity = serde_json::from_str(&s).unwrap();
assert_eq!(back.signature, Some(sample_signature()));
assert_eq!(back.binary_attestation, Some(sample_attestation()));
}
#[test]
fn integrity_roundtrip_with_signature_only() {
let i = Integrity {
content_hash: "sha256:".to_string() + &"0".repeat(64),
binary_hash: None,
binary_verification_url: None,
trace_integrity_chain: serde_json::Value::Null,
signature: Some(sample_signature()),
binary_attestation: None,
cross_period_log: None,
};
let s = serde_json::to_string(&i).unwrap();
let back: Integrity = serde_json::from_str(&s).unwrap();
assert_eq!(back.signature, Some(sample_signature()));
assert!(back.binary_attestation.is_none());
assert!(!s.contains("binary_attestation"));
}
#[test]
fn integrity_roundtrip_with_attestation_only() {
let i = Integrity {
content_hash: "sha256:".to_string() + &"0".repeat(64),
binary_hash: None,
binary_verification_url: None,
trace_integrity_chain: serde_json::Value::Null,
signature: None,
binary_attestation: Some(sample_attestation()),
cross_period_log: None,
};
let s = serde_json::to_string(&i).unwrap();
let back: Integrity = serde_json::from_str(&s).unwrap();
assert!(back.signature.is_none());
assert_eq!(back.binary_attestation, Some(sample_attestation()));
}
#[test]
fn integrity_roundtrip_hash_only_emits_null_signature() {
let i = Integrity {
content_hash: "sha256:".to_string() + &"0".repeat(64),
binary_hash: None,
binary_verification_url: None,
trace_integrity_chain: serde_json::Value::Null,
signature: None,
binary_attestation: None,
cross_period_log: None,
};
let s = serde_json::to_string(&i).unwrap();
assert!(s.contains("\"signature\":null"));
assert!(!s.contains("binary_attestation"));
}
#[test]
fn integrity_level_kebab_serialization_for_new_variant() {
let v = serde_json::to_string(&IntegrityLevel::SignedWithAttestation).unwrap();
assert_eq!(v, "\"signed-with-attestation\"");
let back: IntegrityLevel = serde_json::from_str("\"signed-with-attestation\"").unwrap();
assert_matches!(back, IntegrityLevel::SignedWithAttestation);
}
#[test]
fn default_temporal_coverage_omitted_from_serialization() {
let mut agg = sample_aggregate();
assert!(
serde_json::to_value(&agg)
.unwrap()
.get("temporal_coverage")
.is_some()
);
agg.temporal_coverage = TemporalCoverage::default();
let json = serde_json::to_value(&agg).unwrap();
assert!(json.get("temporal_coverage").is_none());
}
#[test]
fn aggregate_temporal_coverage_defaults_when_missing() {
let legacy = serde_json::json!({
"total_requests": 0,
"total_energy_kwh": 0.0,
"total_carbon_kgco2eq": 0.0,
"aggregate_efficiency_score": 100.0,
"aggregate_waste_ratio": 0.0,
"anti_patterns_detected_count": 0,
"estimated_optimization_potential_kgco2eq": 0.0
});
let agg: Aggregate = serde_json::from_value(legacy).unwrap();
assert!(agg.temporal_coverage.is_default());
assert_eq!(agg.temporal_coverage.observed_days, 0);
}
#[test]
fn coverage_basis_roundtrips_and_is_omitted_when_none() {
let mut scope = sample_scope();
let json = serde_json::to_value(&scope).unwrap();
let basis = json.get("coverage_basis").unwrap();
assert!(basis.get("operator_declared").is_some());
assert!(basis.get("machine_derived").is_some());
scope.coverage_basis = None;
let json = serde_json::to_value(&scope).unwrap();
assert!(json.get("coverage_basis").is_none());
}
#[test]
fn standard_crosswalk_omitted_when_none_and_roundtrips_when_some() {
let mut meth = sample_methodology();
let json = serde_json::to_value(&meth).unwrap();
assert!(json.get("standard_crosswalk").is_none());
meth.standard_crosswalk = Some(StandardCrosswalk::esrs_e1());
let json = serde_json::to_value(&meth).unwrap();
assert!(json.get("standard_crosswalk").is_some());
let back: Methodology = serde_json::from_value(json).unwrap();
assert_eq!(back.standard_crosswalk, Some(StandardCrosswalk::esrs_e1()));
}
#[test]
fn esrs_e1_crosswalk_covers_energy_and_both_carbon_scopes() {
let xw = StandardCrosswalk::esrs_e1();
let datapoints: Vec<&str> = xw.mappings.iter().map(|m| m.datapoint.as_str()).collect();
assert!(
datapoints.iter().any(|d| d.contains("E1-5")),
"energy -> E1-5"
);
assert!(
datapoints.iter().any(|d| d.contains("Scope 2")),
"operational -> Scope 2"
);
assert!(
datapoints.iter().any(|d| d.contains("Scope 3")),
"embodied -> Scope 3"
);
assert!(
xw.mappings.iter().any(|m| m.note.contains("market-based")),
"market-based caveat present"
);
}
#[test]
fn rgesn_criteria_omitted_when_empty() {
let detail = AntiPatternDetail {
kind: "slow_sql".to_string(),
occurrences: 1,
estimated_waste_kwh: 0.0,
estimated_waste_kgco2eq: 0.0,
first_seen: DateTime::parse_from_rfc3339("2026-01-01T00:00:00Z")
.unwrap()
.with_timezone(&Utc),
last_seen: DateTime::parse_from_rfc3339("2026-01-01T00:00:00Z")
.unwrap()
.with_timezone(&Utc),
rgesn_criteria: Vec::new(),
};
let json = serde_json::to_value(&detail).unwrap();
assert!(json.get("rgesn_criteria").is_none());
}
#[test]
fn cross_period_log_reserved_and_absent_in_v1_2() {
let i = sample_integrity();
let s = serde_json::to_string(&i).unwrap();
assert!(!s.contains("cross_period_log"));
let back: Integrity = serde_json::from_str(&s).unwrap();
assert!(back.cross_period_log.is_none());
}
#[test]
fn coverage_basis_field_lists_match_scope_manifest_keys() {
let scope = ScopeManifest {
total_applications_declared: 1,
applications_measured: 1,
applications_excluded: vec![ExcludedApp {
service_name: "x".to_string(),
reason: "y".to_string(),
}],
environments_measured: vec!["prod".to_string()],
environments_excluded: vec![ExcludedEnv {
name: "dev".to_string(),
reason: "z".to_string(),
}],
total_requests_in_period: Some(1),
requests_measured: 1,
coverage_percentage: Some(1.0),
coverage_basis: Some(CoverageBasis::standard()),
};
let value = serde_json::to_value(&scope).unwrap();
let keys = value.as_object().unwrap();
for name in SCOPE_OPERATOR_DECLARED_FIELDS
.iter()
.chain(SCOPE_MACHINE_DERIVED_FIELDS)
{
assert!(
keys.contains_key(*name),
"coverage_basis lists a non-existent ScopeManifest field {name:?}"
);
assert!(
!(SCOPE_OPERATOR_DECLARED_FIELDS.contains(name)
&& SCOPE_MACHINE_DERIVED_FIELDS.contains(name)),
"{name:?} appears in both provenance lists"
);
}
}
}