strings not used internally10

This commit is contained in:
Priec
2026-09-07 10:17:06 +02:00
parent e4364e1c44
commit 042993fafc
14 changed files with 417 additions and 44 deletions

View File

@@ -7,6 +7,12 @@
- Do not run builds, checks, linters, or pre-existing tests. Only run tests that an agent wrote or modified as part of the current task; if the agent did not write or modify any tests, run no tests.
- Check if what you are doing is running. Server can be running, tauri app might be running. No need to turn on redundant systems.
## Typed domain values
- Use enums/domain types for states, kinds, policies, and operations. Parse strings and protobuf integers at boundaries, reject invalid values, and keep internal logic typed.
- Use typed protobuf contracts too; update affected clients, bindings, and descriptors together. Breaking changes are acceptable; add compatibility only when explicitly requested.
- Keep strings for free text, names, storage, language syntax, and display. Diagnostic labels must never control business logic.
## Disk constraints
This workspace is heavily disk constrained. Do not run any command that may create a new or substantially different artifact graph unless the user explicitly authorizes the disk cost.

2
client

Submodule client updated: dc88add49e...0ab21a0764

View File

@@ -11,9 +11,22 @@ service EcbService {
message GetEcbPipelineStatusRequest { int32 batch_limit = 1; }
enum ImportBatchStatus {
IMPORT_BATCH_STATUS_UNSPECIFIED = 0;
IMPORT_BATCH_STATUS_RUNNING = 1;
IMPORT_BATCH_STATUS_SUCCEEDED = 2;
IMPORT_BATCH_STATUS_FAILED = 3;
}
enum CurrencyCoverageStatus {
CURRENCY_COVERAGE_STATUS_UNSPECIFIED = 0;
CURRENCY_COVERAGE_STATUS_PENDING = 1;
CURRENCY_COVERAGE_STATUS_COMPLETE = 2;
}
message EcbImportBatch {
int64 batch_id = 1;
string status = 2;
ImportBatchStatus status = 2;
string requested_from = 3;
string requested_through = 4;
string endpoint = 5;
@@ -27,7 +40,7 @@ message EcbImportBatch {
message EcbCurrencyCoverage {
string currency = 1;
string status = 2;
CurrencyCoverageStatus status = 2;
optional string verified_from_date = 3;
optional string verified_through_date = 4;
}

View File

@@ -167,16 +167,44 @@ message StoredTableScript {
repeated ScriptDependency dependencies = 6;
}
enum ScriptDependencyKind {
SCRIPT_DEPENDENCY_KIND_UNSPECIFIED = 0;
SCRIPT_DEPENDENCY_KIND_COLUMN_ACCESS = 1;
SCRIPT_DEPENDENCY_KIND_RELATED_AGGREGATE = 2;
SCRIPT_DEPENDENCY_KIND_LEDGER_EFFECT = 3;
}
enum AggregateOperation {
AGGREGATE_OPERATION_UNSPECIFIED = 0;
AGGREGATE_OPERATION_SUM = 1;
AGGREGATE_OPERATION_MIN = 2;
AGGREGATE_OPERATION_MAX = 3;
AGGREGATE_OPERATION_COUNT = 4;
AGGREGATE_OPERATION_COUNT_DISTINCT = 5;
AGGREGATE_OPERATION_ANY = 6;
AGGREGATE_OPERATION_ALL = 7;
AGGREGATE_OPERATION_COUNT_ROWS = 8;
AGGREGATE_OPERATION_EXISTS = 9;
}
enum LedgerEffectOperation {
LEDGER_EFFECT_OPERATION_UNSPECIFIED = 0;
LEDGER_EFFECT_OPERATION_ADD = 1;
LEDGER_EFFECT_OPERATION_SUBTRACT = 2;
LEDGER_EFFECT_OPERATION_BOOLEAN = 3;
}
message ScriptDependency {
// Logical table name referenced by the script.
string target_table = 1;
// Normalized dependency kind, such as column_access or related_aggregate.
string dependency_type = 2;
ScriptDependencyKind dependency_type = 2;
// Logical column name. Empty for aggregates that operate on rows only.
string column = 3;
// Aggregate operation name, such as sum, count_rows, or exists; empty for
// column_access dependencies.
string operation = 4;
// Absent for column access; otherwise matches the dependency kind.
oneof operation {
AggregateOperation aggregate_operation = 4;
LedgerEffectOperation ledger_operation = 8;
}
// Relationship table used to match the owner row to the related collection.
string via_table = 5;
// Column of the current row holding the referenced row's id: the link this
@@ -224,9 +252,7 @@ message HydratedColumnValue {
// One declared related-collection aggregate input for the client Steel context.
message HydratedAggregateValue {
// Normalized aggregate operation: sum, min, max, count, count_distinct,
// any, all, count_rows, or exists.
string operation = 1;
AggregateOperation operation = 1;
// Logical table whose related rows were aggregated.
string target_table = 2;
// Logical aggregated column; empty for count_rows and exists.

View File

@@ -7,6 +7,7 @@ pub mod decimal;
pub mod grpc_error;
pub mod money;
pub mod relationship;
pub mod script_operations;
pub mod system_column;
pub mod typst_contract;

Binary file not shown.

View File

@@ -10,8 +10,8 @@ pub struct GetEcbPipelineStatusRequest {
pub struct EcbImportBatch {
#[prost(int64, tag = "1")]
pub batch_id: i64,
#[prost(string, tag = "2")]
pub status: ::prost::alloc::string::String,
#[prost(enumeration = "ImportBatchStatus", tag = "2")]
pub status: i32,
#[prost(string, tag = "3")]
pub requested_from: ::prost::alloc::string::String,
#[prost(string, tag = "4")]
@@ -36,8 +36,8 @@ pub struct EcbImportBatch {
pub struct EcbCurrencyCoverage {
#[prost(string, tag = "1")]
pub currency: ::prost::alloc::string::String,
#[prost(string, tag = "2")]
pub status: ::prost::alloc::string::String,
#[prost(enumeration = "CurrencyCoverageStatus", tag = "2")]
pub status: i32,
#[prost(string, optional, tag = "3")]
pub verified_from_date: ::core::option::Option<::prost::alloc::string::String>,
#[prost(string, optional, tag = "4")]
@@ -73,6 +73,69 @@ pub struct GetEcbPipelineStatusResponse {
#[prost(message, repeated, tag = "11")]
pub currency_coverage: ::prost::alloc::vec::Vec<EcbCurrencyCoverage>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum ImportBatchStatus {
Unspecified = 0,
Running = 1,
Succeeded = 2,
Failed = 3,
}
impl ImportBatchStatus {
/// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
Self::Unspecified => "IMPORT_BATCH_STATUS_UNSPECIFIED",
Self::Running => "IMPORT_BATCH_STATUS_RUNNING",
Self::Succeeded => "IMPORT_BATCH_STATUS_SUCCEEDED",
Self::Failed => "IMPORT_BATCH_STATUS_FAILED",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"IMPORT_BATCH_STATUS_UNSPECIFIED" => Some(Self::Unspecified),
"IMPORT_BATCH_STATUS_RUNNING" => Some(Self::Running),
"IMPORT_BATCH_STATUS_SUCCEEDED" => Some(Self::Succeeded),
"IMPORT_BATCH_STATUS_FAILED" => Some(Self::Failed),
_ => None,
}
}
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum CurrencyCoverageStatus {
Unspecified = 0,
Pending = 1,
Complete = 2,
}
impl CurrencyCoverageStatus {
/// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
Self::Unspecified => "CURRENCY_COVERAGE_STATUS_UNSPECIFIED",
Self::Pending => "CURRENCY_COVERAGE_STATUS_PENDING",
Self::Complete => "CURRENCY_COVERAGE_STATUS_COMPLETE",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"CURRENCY_COVERAGE_STATUS_UNSPECIFIED" => Some(Self::Unspecified),
"CURRENCY_COVERAGE_STATUS_PENDING" => Some(Self::Pending),
"CURRENCY_COVERAGE_STATUS_COMPLETE" => Some(Self::Complete),
_ => None,
}
}
}
/// Generated client implementations.
pub mod ecb_service_client {
#![allow(

View File

@@ -117,16 +117,11 @@ pub struct ScriptDependency {
/// Logical table name referenced by the script.
#[prost(string, tag = "1")]
pub target_table: ::prost::alloc::string::String,
/// Normalized dependency kind, such as column_access or related_aggregate.
#[prost(string, tag = "2")]
pub dependency_type: ::prost::alloc::string::String,
#[prost(enumeration = "ScriptDependencyKind", tag = "2")]
pub dependency_type: i32,
/// Logical column name. Empty for aggregates that operate on rows only.
#[prost(string, tag = "3")]
pub column: ::prost::alloc::string::String,
/// Aggregate operation name, such as sum, count_rows, or exists; empty for
/// column_access dependencies.
#[prost(string, tag = "4")]
pub operation: ::prost::alloc::string::String,
/// Relationship table used to match the owner row to the related collection.
#[prost(string, tag = "5")]
pub via_table: ::prost::alloc::string::String,
@@ -142,6 +137,21 @@ pub struct ScriptDependency {
/// runtime looks its inputs up by this text.
#[prost(string, tag = "7")]
pub name_in_script: ::prost::alloc::string::String,
/// Absent for column access; otherwise matches the dependency kind.
#[prost(oneof = "script_dependency::Operation", tags = "4, 8")]
pub operation: ::core::option::Option<script_dependency::Operation>,
}
/// Nested message and enum types in `ScriptDependency`.
pub mod script_dependency {
/// Absent for column access; otherwise matches the dependency kind.
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Oneof)]
pub enum Operation {
#[prost(enumeration = "super::AggregateOperation", tag = "4")]
AggregateOperation(i32),
#[prost(enumeration = "super::LedgerEffectOperation", tag = "8")]
LedgerOperation(i32),
}
}
/// Identifies the active form row whose external Steel inputs must be hydrated.
#[derive(serde::Serialize, serde::Deserialize)]
@@ -193,10 +203,8 @@ pub struct HydratedColumnValue {
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct HydratedAggregateValue {
/// Normalized aggregate operation: sum, min, max, count, count_distinct,
/// any, all, count_rows, or exists.
#[prost(string, tag = "1")]
pub operation: ::prost::alloc::string::String,
#[prost(enumeration = "AggregateOperation", tag = "1")]
pub operation: i32,
/// Logical table whose related rows were aggregated.
#[prost(string, tag = "2")]
pub target_table: ::prost::alloc::string::String,
@@ -227,6 +235,123 @@ pub struct HydrateScriptDependenciesResponse {
#[prost(message, repeated, tag = "2")]
pub aggregates: ::prost::alloc::vec::Vec<HydratedAggregateValue>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum ScriptDependencyKind {
Unspecified = 0,
ColumnAccess = 1,
RelatedAggregate = 2,
LedgerEffect = 3,
}
impl ScriptDependencyKind {
/// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
Self::Unspecified => "SCRIPT_DEPENDENCY_KIND_UNSPECIFIED",
Self::ColumnAccess => "SCRIPT_DEPENDENCY_KIND_COLUMN_ACCESS",
Self::RelatedAggregate => "SCRIPT_DEPENDENCY_KIND_RELATED_AGGREGATE",
Self::LedgerEffect => "SCRIPT_DEPENDENCY_KIND_LEDGER_EFFECT",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"SCRIPT_DEPENDENCY_KIND_UNSPECIFIED" => Some(Self::Unspecified),
"SCRIPT_DEPENDENCY_KIND_COLUMN_ACCESS" => Some(Self::ColumnAccess),
"SCRIPT_DEPENDENCY_KIND_RELATED_AGGREGATE" => Some(Self::RelatedAggregate),
"SCRIPT_DEPENDENCY_KIND_LEDGER_EFFECT" => Some(Self::LedgerEffect),
_ => None,
}
}
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum AggregateOperation {
Unspecified = 0,
Sum = 1,
Min = 2,
Max = 3,
Count = 4,
CountDistinct = 5,
Any = 6,
All = 7,
CountRows = 8,
Exists = 9,
}
impl AggregateOperation {
/// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
Self::Unspecified => "AGGREGATE_OPERATION_UNSPECIFIED",
Self::Sum => "AGGREGATE_OPERATION_SUM",
Self::Min => "AGGREGATE_OPERATION_MIN",
Self::Max => "AGGREGATE_OPERATION_MAX",
Self::Count => "AGGREGATE_OPERATION_COUNT",
Self::CountDistinct => "AGGREGATE_OPERATION_COUNT_DISTINCT",
Self::Any => "AGGREGATE_OPERATION_ANY",
Self::All => "AGGREGATE_OPERATION_ALL",
Self::CountRows => "AGGREGATE_OPERATION_COUNT_ROWS",
Self::Exists => "AGGREGATE_OPERATION_EXISTS",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"AGGREGATE_OPERATION_UNSPECIFIED" => Some(Self::Unspecified),
"AGGREGATE_OPERATION_SUM" => Some(Self::Sum),
"AGGREGATE_OPERATION_MIN" => Some(Self::Min),
"AGGREGATE_OPERATION_MAX" => Some(Self::Max),
"AGGREGATE_OPERATION_COUNT" => Some(Self::Count),
"AGGREGATE_OPERATION_COUNT_DISTINCT" => Some(Self::CountDistinct),
"AGGREGATE_OPERATION_ANY" => Some(Self::Any),
"AGGREGATE_OPERATION_ALL" => Some(Self::All),
"AGGREGATE_OPERATION_COUNT_ROWS" => Some(Self::CountRows),
"AGGREGATE_OPERATION_EXISTS" => Some(Self::Exists),
_ => None,
}
}
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum LedgerEffectOperation {
Unspecified = 0,
Add = 1,
Subtract = 2,
Boolean = 3,
}
impl LedgerEffectOperation {
/// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
Self::Unspecified => "LEDGER_EFFECT_OPERATION_UNSPECIFIED",
Self::Add => "LEDGER_EFFECT_OPERATION_ADD",
Self::Subtract => "LEDGER_EFFECT_OPERATION_SUBTRACT",
Self::Boolean => "LEDGER_EFFECT_OPERATION_BOOLEAN",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"LEDGER_EFFECT_OPERATION_UNSPECIFIED" => Some(Self::Unspecified),
"LEDGER_EFFECT_OPERATION_ADD" => Some(Self::Add),
"LEDGER_EFFECT_OPERATION_SUBTRACT" => Some(Self::Subtract),
"LEDGER_EFFECT_OPERATION_BOOLEAN" => Some(Self::Boolean),
_ => None,
}
}
}
/// Generated client implementations.
pub mod table_script_client {
#![allow(

View File

@@ -0,0 +1,134 @@
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AggregateOperation {
Sum,
Min,
Max,
Count,
CountDistinct,
Any,
All,
CountRows,
Exists,
}
impl AggregateOperation {
pub fn as_str(self) -> &'static str {
match self {
Self::Sum => "sum",
Self::Min => "min",
Self::Max => "max",
Self::Count => "count",
Self::CountDistinct => "count_distinct",
Self::Any => "any",
Self::All => "all",
Self::CountRows => "count_rows",
Self::Exists => "exists",
}
}
}
impl std::str::FromStr for AggregateOperation {
type Err = serde::de::value::Error;
fn from_str(value: &str) -> Result<Self, Self::Err> {
Self::deserialize(serde::de::value::StrDeserializer::new(value))
}
}
impl std::fmt::Display for AggregateOperation {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(self.as_str())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LedgerEffectOperation {
Add,
#[serde(rename = "sub")]
Subtract,
Boolean,
}
impl LedgerEffectOperation {
pub fn as_str(self) -> &'static str {
match self {
Self::Add => "add",
Self::Subtract => "sub",
Self::Boolean => "boolean",
}
}
}
impl std::fmt::Display for LedgerEffectOperation {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(self.as_str())
}
}
impl From<AggregateOperation> for crate::proto::komp_ac::table_script::AggregateOperation {
fn from(operation: AggregateOperation) -> Self {
match operation {
AggregateOperation::Sum => Self::Sum,
AggregateOperation::Min => Self::Min,
AggregateOperation::Max => Self::Max,
AggregateOperation::Count => Self::Count,
AggregateOperation::CountDistinct => Self::CountDistinct,
AggregateOperation::Any => Self::Any,
AggregateOperation::All => Self::All,
AggregateOperation::CountRows => Self::CountRows,
AggregateOperation::Exists => Self::Exists,
}
}
}
impl TryFrom<i32> for AggregateOperation {
type Error = serde::de::value::Error;
fn try_from(value: i32) -> Result<Self, Self::Error> {
use crate::proto::komp_ac::table_script::AggregateOperation as ProtoOperation;
match ProtoOperation::try_from(value) {
Ok(ProtoOperation::Sum) => Ok(Self::Sum),
Ok(ProtoOperation::Min) => Ok(Self::Min),
Ok(ProtoOperation::Max) => Ok(Self::Max),
Ok(ProtoOperation::Count) => Ok(Self::Count),
Ok(ProtoOperation::CountDistinct) => Ok(Self::CountDistinct),
Ok(ProtoOperation::Any) => Ok(Self::Any),
Ok(ProtoOperation::All) => Ok(Self::All),
Ok(ProtoOperation::CountRows) => Ok(Self::CountRows),
Ok(ProtoOperation::Exists) => Ok(Self::Exists),
_ => Err(serde::de::Error::custom(format!(
"Invalid AggregateOperation: {value}"
))),
}
}
}
impl From<LedgerEffectOperation> for crate::proto::komp_ac::table_script::LedgerEffectOperation {
fn from(operation: LedgerEffectOperation) -> Self {
match operation {
LedgerEffectOperation::Add => Self::Add,
LedgerEffectOperation::Subtract => Self::Subtract,
LedgerEffectOperation::Boolean => Self::Boolean,
}
}
}
impl TryFrom<i32> for LedgerEffectOperation {
type Error = serde::de::value::Error;
fn try_from(value: i32) -> Result<Self, Self::Error> {
use crate::proto::komp_ac::table_script::LedgerEffectOperation as ProtoOperation;
match ProtoOperation::try_from(value) {
Ok(ProtoOperation::Add) => Ok(Self::Add),
Ok(ProtoOperation::Subtract) => Ok(Self::Subtract),
Ok(ProtoOperation::Boolean) => Ok(Self::Boolean),
_ => Err(serde::de::Error::custom(format!(
"Invalid LedgerEffectOperation: {value}"
))),
}
}
}

2
server

Submodule server updated: 8dc3686366...6d6f241b0b

View File

@@ -71,9 +71,14 @@ pub(crate) async fn load_ecb_page(
batches: status
.batches
.into_iter()
.map(|batch| ImportBatchView {
.map(|batch| Ok(ImportBatchView {
batch_id: batch.batch_id,
status: batch.status,
status: match crate::ecb::ImportBatchStatus::try_from(batch.status) {
Ok(status @ (crate::ecb::ImportBatchStatus::Running
| crate::ecb::ImportBatchStatus::Succeeded
| crate::ecb::ImportBatchStatus::Failed)) => status,
_ => return Err(LoadError::Backend(format!("Invalid import batch status: {}", batch.status))),
},
requested_from: batch.requested_from,
requested_through: batch.requested_through,
endpoint: batch.endpoint,
@@ -83,7 +88,7 @@ pub(crate) async fn load_ecb_page(
observation_count: batch.observation_count,
inserted_observation_count: batch.inserted_observation_count,
error_message: batch.error_message,
})
.collect(),
}))
.collect::<Result<Vec<_>, LoadError>>()?,
})
}

View File

@@ -77,7 +77,7 @@ fn from_now(locale: Locale, raw: &str) -> Option<String> {
/// One import attempt, as the audit log recorded it.
pub(crate) struct ImportBatchView {
pub batch_id: i64,
pub status: String,
pub status: crate::ecb::ImportBatchStatus,
pub requested_from: String,
pub requested_through: String,
pub endpoint: String,
@@ -91,15 +91,15 @@ pub(crate) struct ImportBatchView {
impl ImportBatchView {
pub(crate) fn succeeded(&self) -> bool {
self.status == "succeeded"
self.status == crate::ecb::ImportBatchStatus::Succeeded
}
pub(crate) fn running(&self) -> bool {
self.status == "running"
self.status == crate::ecb::ImportBatchStatus::Running
}
pub(crate) fn failed(&self) -> bool {
self.status == "failed"
self.status == crate::ecb::ImportBatchStatus::Failed
}
pub(crate) fn started(&self) -> String {

View File

@@ -63,10 +63,10 @@ mod tests {
use super::*;
use crate::pages::admin::ecb::state::ImportBatchView;
fn batch(id: i64, status: &str) -> ImportBatchView {
fn batch(id: i64, status: crate::ecb::ImportBatchStatus) -> ImportBatchView {
ImportBatchView {
batch_id: id,
status: status.to_string(),
status,
requested_from: "2026-08-10".to_string(),
requested_through: "2026-08-12".to_string(),
endpoint: "https://data.ecb.europa.eu/...".to_string(),
@@ -87,7 +87,7 @@ mod tests {
days_behind: 0,
import_running: false,
next_import_at: "2026-08-13T15:00:00Z".to_string(),
batches: vec![batch(9, "succeeded")],
batches: vec![batch(9, crate::ecb::ImportBatchStatus::Succeeded)],
covered_currencies: vec!["CZK".to_string(), "USD".to_string()],
transactions_postable_through: Some("2026-08-13".to_string()),
statements_postable_through: Some("2026-08-12".to_string()),
@@ -143,8 +143,8 @@ mod tests {
#[test]
fn historical_failures_are_collapsed_as_technical_history() {
let mut page = healthy_page();
page.batches.push(batch(8, "failed"));
page.batches.push(batch(7, "failed"));
page.batches.push(batch(8, crate::ecb::ImportBatchStatus::Failed));
page.batches.push(batch(7, crate::ecb::ImportBatchStatus::Failed));
let html = render_page(&page);
@@ -196,7 +196,7 @@ mod tests {
fn a_running_import_polls_until_it_finishes() {
let mut page = healthy_page();
page.import_running = true;
page.batches.insert(0, batch(10, "running"));
page.batches.insert(0, batch(10, crate::ecb::ImportBatchStatus::Running));
let html = render_page(&page);
assert!(!html.contains("Template error"), "{html}");
@@ -219,13 +219,13 @@ mod tests {
let mut page = healthy_page();
page.healthy = false;
page.batches = vec![ImportBatchView {
status: "failed".to_string(),
status: crate::ecb::ImportBatchStatus::Failed,
completed_at: None,
verified_through_date: None,
observation_count: None,
inserted_observation_count: None,
error_message: Some("the ECB endpoint returned 503".to_string()),
..batch(11, "failed")
..batch(11, crate::ecb::ImportBatchStatus::Failed)
}];
let html = render_page(&page);