v0.3.7-pre.012
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<!-- file: crates/ksp-app-backfill-desk/frontend/main.html -->
|
||||
<!-- version: 7 -->
|
||||
<!-- version: 8 -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
@@ -87,7 +87,9 @@
|
||||
</div>
|
||||
<div id="backfillAddressGroup" class="col-12">
|
||||
<label for="backfillAddress" class="form-label">Adresse Solana</label>
|
||||
<input id="backfillAddress" class="form-control font-monospace" type="text" autocomplete="off" spellcheck="false">
|
||||
<input id="backfillAddress" class="form-control font-monospace" type="text" list="backfillProgramIds" autocomplete="off" spellcheck="false" aria-describedby="backfillProgramIdAutocompleteHelp">
|
||||
<datalist id="backfillProgramIds"></datalist>
|
||||
<div id="backfillProgramIdAutocompleteHelp" class="form-text">Suggestions issues du registre canonique KSP ; toute adresse Solana valide reste acceptée.</div>
|
||||
</div>
|
||||
<div id="backfillAnchorGroup" class="col-12" hidden>
|
||||
<label for="backfillAnchorSignature" class="form-label">Signature d'ancrage exclusive</label>
|
||||
@@ -149,6 +151,7 @@
|
||||
<div class="card-body">
|
||||
<div id="backfillCancelFeedback" class="alert alert-secondary py-2" role="status" aria-live="polite" hidden></div>
|
||||
<div id="backfillResumeFeedback" class="alert alert-secondary py-2" role="status" aria-live="polite" hidden></div>
|
||||
<div id="backfillTerminalSummary" class="alert alert-secondary py-2" role="status" aria-live="polite" hidden></div>
|
||||
<dl class="row mb-0 app-runtime-list">
|
||||
<dt class="col-sm-5">Job</dt><dd id="runStatusJobId" class="col-sm-7 font-monospace">—</dd>
|
||||
<dt class="col-sm-5">Lifecycle</dt><dd id="runStatusLifecycle" class="col-sm-7">—</dd>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/frontend/ts/main.ts
|
||||
// version: 7
|
||||
// version: 8
|
||||
|
||||
import "bootstrap";
|
||||
import ResizeObserver from "resize-observer-polyfill";
|
||||
@@ -14,6 +14,7 @@ import type { BackfillStartRequestDto } from "./bindings/ksp_app_backfill_desk/d
|
||||
import type { BackfillStartResponseDto } from "./bindings/ksp_app_backfill_desk/dto_backfill/BackfillStartResponseDto.ts";
|
||||
import type { BackfillDeskOptionsDto } from "./bindings/ksp_app_backfill_desk/dto_common/BackfillDeskOptionsDto.ts";
|
||||
import type { BackfillHttpRouteOptionDto } from "./bindings/ksp_app_backfill_desk/dto_common/BackfillHttpRouteOptionDto.ts";
|
||||
import type { ProgramIdAutocompleteOptionDto } from "./bindings/ksp_app_backfill_desk/dto_common/ProgramIdAutocompleteOptionDto.ts";
|
||||
import type { ShellStatusDto } from "./bindings/ksp_app_backfill_desk/dto_common/ShellStatusDto.ts";
|
||||
import { frontendDebug, frontendInfo, frontendTrace, frontendWarn, installFrontendConsoleBridge } from "./frontend_log";
|
||||
import { invokeKsp } from "./invoke";
|
||||
@@ -141,7 +142,56 @@ function refreshStartButton(): void {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function renderProgramIdAutocomplete(options: ProgramIdAutocompleteOptionDto[]): void {
|
||||
const datalist = document.querySelector<HTMLDataListElement>("#backfillProgramIds");
|
||||
if (!datalist) {
|
||||
return;
|
||||
}
|
||||
datalist.replaceChildren();
|
||||
for (const program of options) {
|
||||
const option = document.createElement("option");
|
||||
option.value = program.programId;
|
||||
option.label = `${program.name} — ${program.code}`;
|
||||
option.dataset.code = program.code;
|
||||
option.dataset.domain = program.domain;
|
||||
option.dataset.family = program.family;
|
||||
option.dataset.name = program.name;
|
||||
option.dataset.protocol = program.protocol;
|
||||
datalist.append(option);
|
||||
}
|
||||
const help = document.querySelector<HTMLElement>("#backfillProgramIdAutocompleteHelp");
|
||||
if (help) {
|
||||
help.textContent = `${options.length} Program ID KSP disponibles comme suggestions ; la saisie reste libre.`;
|
||||
}
|
||||
frontendTrace("main", "Backfill Desk Program ID autocomplete dataset rendered", { optionCount: options.length });
|
||||
}
|
||||
|
||||
function selectedProgramAutocompleteEntry(address: string): HTMLOptionElement | null {
|
||||
const datalist = document.querySelector<HTMLDataListElement>("#backfillProgramIds");
|
||||
if (!datalist) {
|
||||
return null;
|
||||
}
|
||||
for (const option of Array.from(datalist.options)) {
|
||||
if (option.value === address) {
|
||||
return option;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function safeCommandErrorText(caughtError: unknown, fallback: string): string {
|
||||
if (typeof caughtError === "object" && caughtError !== null) {
|
||||
const candidate = caughtError as { code?: unknown; domain?: unknown; message?: unknown };
|
||||
if (typeof candidate.domain === "string" && typeof candidate.code === "string" && typeof candidate.message === "string") {
|
||||
return `${candidate.domain}/${candidate.code}: ${candidate.message}`;
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function renderCampaignContract(options: BackfillDeskOptionsDto): void {
|
||||
renderProgramIdAutocomplete(options.programIdOptions);
|
||||
populateCodeSelect("backfillScopeKind", options.scopeKinds, scopeLabels);
|
||||
populateCodeSelect("backfillCommitment", options.commitments, commitmentLabels);
|
||||
applyNumberInputContract("backfillPageSize", options.limits.maxPageSize, options.limits.defaultPageSize);
|
||||
@@ -296,13 +346,7 @@ function renderRequestValidationError(caughtError: unknown): void {
|
||||
if (!status) {
|
||||
return;
|
||||
}
|
||||
let message = "Requête refusée par le backend.";
|
||||
if (typeof caughtError === "object" && caughtError !== null) {
|
||||
const candidate = caughtError as { code?: unknown; domain?: unknown; message?: unknown };
|
||||
if (typeof candidate.domain === "string" && typeof candidate.code === "string" && typeof candidate.message === "string") {
|
||||
message = `${candidate.domain}/${candidate.code}: ${candidate.message}`;
|
||||
}
|
||||
}
|
||||
const message = safeCommandErrorText(caughtError, "Requête refusée par le backend.");
|
||||
status.hidden = false;
|
||||
status.classList.remove("alert-secondary", "alert-success");
|
||||
status.classList.add("alert-danger");
|
||||
@@ -373,6 +417,16 @@ function bindCampaignForm(): void {
|
||||
void validateBackfillRequest();
|
||||
});
|
||||
}
|
||||
const address = document.querySelector<HTMLInputElement>("#backfillAddress");
|
||||
if (address) {
|
||||
address.addEventListener("change", () => {
|
||||
const selected = selectedProgramAutocompleteEntry(address.value);
|
||||
frontendTrace("main", "Backfill Desk address autocomplete selection evaluated", {
|
||||
programCode: selected?.dataset.code ?? null,
|
||||
registryMatch: selected !== null,
|
||||
});
|
||||
});
|
||||
}
|
||||
const startButton = document.querySelector<HTMLButtonElement>("#startBackfillRequest");
|
||||
if (startButton) {
|
||||
startButton.addEventListener("click", () => {
|
||||
@@ -472,6 +526,11 @@ function renderBackfillRunStatus(status: BackfillRunStatusDto | null, source: "c
|
||||
if (card) {
|
||||
card.hidden = true;
|
||||
}
|
||||
const terminalSummary = document.querySelector<HTMLElement>("#backfillTerminalSummary");
|
||||
if (terminalSummary) {
|
||||
terminalSummary.hidden = true;
|
||||
terminalSummary.textContent = "";
|
||||
}
|
||||
frontendTrace("main", "Backfill Desk latest-value status cleared", { source });
|
||||
return;
|
||||
}
|
||||
@@ -489,6 +548,27 @@ function renderBackfillRunStatus(status: BackfillRunStatusDto | null, source: "c
|
||||
if (card) {
|
||||
card.hidden = false;
|
||||
}
|
||||
const terminalSummary = document.querySelector<HTMLElement>("#backfillTerminalSummary");
|
||||
if (terminalSummary) {
|
||||
terminalSummary.hidden = !status.terminal;
|
||||
terminalSummary.classList.remove("alert-success", "alert-warning", "alert-danger", "alert-secondary");
|
||||
if (status.terminal) {
|
||||
const failure = status.failureDomain === null || status.failureCode === null ? null : `${status.failureDomain}/${status.failureCode}`;
|
||||
if (status.state === "completed") {
|
||||
terminalSummary.classList.add("alert-success");
|
||||
terminalSummary.textContent = `Campagne terminée — ${status.contiguousCompleted} candidats contigus durablement traités.`;
|
||||
} else if (status.state === "cancelled") {
|
||||
terminalSummary.classList.add("alert-warning");
|
||||
terminalSummary.textContent = `Campagne annulée coopérativement — checkpoint ${status.checkpointPresent ? "disponible" : "absent"}.`;
|
||||
} else {
|
||||
terminalSummary.classList.add("alert-danger");
|
||||
terminalSummary.textContent = `Campagne terminée en échec${failure === null ? "" : ` — ${failure}`}.`;
|
||||
}
|
||||
} else {
|
||||
terminalSummary.classList.add("alert-secondary");
|
||||
terminalSummary.textContent = "";
|
||||
}
|
||||
}
|
||||
const values: Record<string, string> = {
|
||||
runStatusJobId: status.jobId,
|
||||
runStatusLifecycle: status.completion === null ? status.state : `${status.state} / ${status.completion}`,
|
||||
@@ -552,11 +632,18 @@ async function resumeBackfillRun(): Promise<void> {
|
||||
state: response.state,
|
||||
});
|
||||
await syncBackfillStatus("resume");
|
||||
} catch (_caughtError) {
|
||||
} catch (caughtError) {
|
||||
resumeInFlight = false;
|
||||
if (resumeButton && currentRunStatus?.terminal && currentRunStatus.checkpointPresent) {
|
||||
resumeButton.disabled = false;
|
||||
}
|
||||
const feedback = document.querySelector<HTMLElement>("#backfillResumeFeedback");
|
||||
if (feedback) {
|
||||
feedback.hidden = false;
|
||||
feedback.classList.remove("alert-secondary", "alert-success");
|
||||
feedback.classList.add("alert-danger");
|
||||
feedback.textContent = safeCommandErrorText(caughtError, "La reprise du checkpoint a été refusée.");
|
||||
}
|
||||
frontendWarn("main", "Backfill Desk in-session Resume failed");
|
||||
}
|
||||
}
|
||||
@@ -597,7 +684,14 @@ async function bindBackfillStatusMonitoring(): Promise<void> {
|
||||
});
|
||||
return syncBackfillStatus("cancel");
|
||||
})
|
||||
.catch(() => {
|
||||
.catch(caughtError => {
|
||||
const feedback = document.querySelector<HTMLElement>("#backfillCancelFeedback");
|
||||
if (feedback) {
|
||||
feedback.hidden = false;
|
||||
feedback.classList.remove("alert-secondary", "alert-success");
|
||||
feedback.classList.add("alert-danger");
|
||||
feedback.textContent = safeCommandErrorText(caughtError, "La demande d'annulation a été refusée.");
|
||||
}
|
||||
frontendWarn("main", "Backfill Desk cooperative cancellation failed");
|
||||
if (currentRunStatus?.active) {
|
||||
cancelButton.disabled = false;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/app_state.rs
|
||||
// version: 9
|
||||
// version: 10
|
||||
|
||||
//! Shared backend state owned by the Backfill Desk Tauri application.
|
||||
|
||||
@@ -123,7 +123,7 @@ impl crate::AppState {
|
||||
application_version: env!("CARGO_PKG_VERSION").to_owned(),
|
||||
config_document_count: document_count,
|
||||
fallback_logging_active: runtime.fallback_active,
|
||||
shell_phase: "pre.011-resume-checkpoint".to_owned(),
|
||||
shell_phase: "pre.012-frontend-polish".to_owned(),
|
||||
startup_diagnostic: runtime.startup_diagnostic.clone(),
|
||||
});
|
||||
}
|
||||
@@ -146,6 +146,7 @@ impl crate::AppState {
|
||||
http_routes: std::vec::Vec::new(),
|
||||
limits,
|
||||
network_coherent: false,
|
||||
program_id_options: crate::program_id_autocomplete_options(),
|
||||
scope_kinds: crate::backfill_scope_kind_codes(),
|
||||
store_diagnostic: std::option::Option::None,
|
||||
store_network: std::option::Option::None,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/dto_common.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
//! Common Tauri DTOs shared by the Backfill Desk shell.
|
||||
|
||||
@@ -21,6 +21,45 @@ pub(crate) struct BackfillHttpRouteOptionDto {
|
||||
pub(crate) role: String,
|
||||
}
|
||||
|
||||
/// Safe Program ID entry exposed only to populate the free-form address autocomplete dataset.
|
||||
///
|
||||
/// Every value is derived from the canonical `ksp-core-lib` registry. The frontend remains free to submit any valid Solana address; this DTO is advisory only.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_backfill_desk/dto_common/ProgramIdAutocompleteOptionDto.ts")]
|
||||
pub(crate) struct ProgramIdAutocompleteOptionDto {
|
||||
/// Stable KSP machine-readable Program ID code.
|
||||
pub(crate) code: String,
|
||||
/// Broad functional domain from the canonical registry.
|
||||
pub(crate) domain: String,
|
||||
/// Functional family from the canonical registry.
|
||||
pub(crate) family: String,
|
||||
/// Human-readable program name.
|
||||
pub(crate) name: String,
|
||||
/// Canonical Base58 Program ID used as the HTML datalist value.
|
||||
pub(crate) program_id: String,
|
||||
/// Owning protocol/project label from the canonical registry.
|
||||
pub(crate) protocol: String,
|
||||
}
|
||||
|
||||
/// Projects the canonical KSP Program ID registry to the safe autocomplete dataset.
|
||||
#[must_use]
|
||||
pub(crate) fn program_id_autocomplete_options() -> std::vec::Vec<ProgramIdAutocompleteOptionDto> {
|
||||
let entries = ksp_core_lib::entries();
|
||||
let mut options = std::vec::Vec::with_capacity(entries.len());
|
||||
for entry in entries {
|
||||
options.push(ProgramIdAutocompleteOptionDto {
|
||||
code: entry.code().to_owned(),
|
||||
domain: entry.domain().to_owned(),
|
||||
family: entry.family().to_owned(),
|
||||
name: entry.name().to_owned(),
|
||||
program_id: entry.program_id().to_owned(),
|
||||
protocol: entry.protocol().to_owned(),
|
||||
});
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
/// Safe Backfill Desk options contract combining readiness, HTTP routes and Job-owned campaign bounds.
|
||||
///
|
||||
/// Endpoint URLs, credentials and physical routing details are intentionally absent.
|
||||
@@ -36,6 +75,8 @@ pub(crate) struct BackfillDeskOptionsDto {
|
||||
pub(crate) limits: crate::BackfillRequestLimitsDto,
|
||||
/// Whether the Transport and Store readiness gates jointly permit later Backfill composition.
|
||||
pub(crate) composition_ready: bool,
|
||||
/// Canonical KSP Program IDs exposed as a frontend autocomplete dataset; never an allow-list.
|
||||
pub(crate) program_id_options: std::vec::Vec<ProgramIdAutocompleteOptionDto>,
|
||||
/// Distinct enabled HTTP cluster labels selected by the active Transport profile.
|
||||
pub(crate) configured_networks: std::vec::Vec<String>,
|
||||
/// Whether the selected Store network exactly matches the one coherent Transport network.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/lib.rs
|
||||
// version: 10
|
||||
// version: 11
|
||||
|
||||
//! Tauri desktop application scaffold for controlling and inspecting KSP RAW backfill jobs.
|
||||
|
||||
@@ -116,8 +116,12 @@ pub(crate) use self::dto_common::BackfillDeskOptionsDto;
|
||||
pub(crate) use self::dto_common::BackfillHttpRouteOptionDto;
|
||||
/// Safe command error projection exposed to Tauri commands.
|
||||
pub(crate) use self::dto_common::CommandErrorDto;
|
||||
/// Safe canonical Program ID entry exposed only for free-form address autocomplete.
|
||||
pub(crate) use self::dto_common::ProgramIdAutocompleteOptionDto;
|
||||
/// Safe scaffold/runtime snapshot exposed to the shell.
|
||||
pub(crate) use self::dto_common::ShellStatusDto;
|
||||
/// Projects the canonical Core Program ID registry to autocomplete entries.
|
||||
pub(crate) use self::dto_common::program_id_autocomplete_options;
|
||||
/// Shared Backfill Desk runtime state is internally inconsistent.
|
||||
pub(crate) use self::errors::ERROR_CODE_APP_STATE_INVALID;
|
||||
/// Shared Backfill Desk runtime state cannot be locked safely.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/transport_runtime.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
//! Composite-selected HTTP Transport readiness and route inventory owned by Backfill Desk.
|
||||
|
||||
@@ -54,6 +54,7 @@ impl TransportRuntime {
|
||||
http_routes,
|
||||
limits,
|
||||
network_coherent: false,
|
||||
program_id_options: crate::program_id_autocomplete_options(),
|
||||
scope_kinds: crate::backfill_scope_kind_codes(),
|
||||
store_diagnostic: std::option::Option::None,
|
||||
store_network: std::option::Option::None,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/tests/desktop_contract.rs
|
||||
// version: 13
|
||||
// version: 14
|
||||
|
||||
//! Structural desktop contract checks for the Backfill Desk scaffold.
|
||||
|
||||
@@ -404,3 +404,33 @@ fn pre_011_resume_reissues_rust_only_checkpoint_for_new_backend_job_without_ipc_
|
||||
assert!(html.contains("resumeBackfillRun"));
|
||||
assert!(html.contains("Reprendre"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_012_free_address_autocomplete_is_html_datalist_derived_from_core_registry() {
|
||||
let root = app_root();
|
||||
let dto = read_text(root.join("src/dto_common.rs").as_path());
|
||||
assert!(dto.contains("ksp_core_lib::entries()"));
|
||||
assert!(dto.contains("ProgramIdAutocompleteOptionDto"));
|
||||
assert!(dto.contains("program_id_autocomplete_options"));
|
||||
let html = read_text(root.join("frontend/main.html").as_path());
|
||||
assert!(html.contains("list=\"backfillProgramIds\""));
|
||||
assert!(html.contains("<datalist id=\"backfillProgramIds\"></datalist>"));
|
||||
assert!(html.contains("toute adresse Solana valide reste acceptée"));
|
||||
let frontend = read_text(root.join("frontend/ts/main.ts").as_path());
|
||||
for required in [
|
||||
"renderProgramIdAutocomplete(options.programIdOptions)",
|
||||
"option.value = program.programId",
|
||||
"option.dataset.code = program.code",
|
||||
"option.dataset.domain = program.domain",
|
||||
"registryMatch: selected !== null",
|
||||
"Backfill Desk Program ID autocomplete dataset rendered",
|
||||
] {
|
||||
assert!(frontend.contains(required), "missing pre.012 autocomplete marker {required}");
|
||||
}
|
||||
for forbidden in
|
||||
["AddressLookupTab1e1111111111111111111111111", "ComputeBudget111111111111111111111111111111", "Vote111111111111111111111111111111111111111"]
|
||||
{
|
||||
assert!(!frontend.contains(forbidden), "frontend hardcodes canonical Program ID {forbidden}");
|
||||
assert!(!html.contains(forbidden), "HTML hardcodes canonical Program ID {forbidden}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/tests/desktop_security.rs
|
||||
// version: 14
|
||||
// version: 15
|
||||
|
||||
//! Security and dependency-boundary checks for the Backfill Desk scaffold.
|
||||
|
||||
@@ -339,3 +339,29 @@ fn pre_011_resume_surface_exposes_only_new_job_acknowledgement_and_never_checkpo
|
||||
assert!(!resume_source.contains(forbidden), "Resume frontend sends or logs forbidden payload marker {forbidden}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_012_program_id_autocomplete_exposes_only_public_registry_metadata_and_no_browser_storage() {
|
||||
let root = app_root();
|
||||
let dto_source = read_text(root.join("src/dto_common.rs").as_path());
|
||||
let dto = struct_source(dto_source.as_str(), "ProgramIdAutocompleteOptionDto");
|
||||
for required in ["code", "domain", "family", "name", "program_id", "protocol"] {
|
||||
assert!(dto.contains(required), "Program ID autocomplete DTO missing safe registry field {required}");
|
||||
}
|
||||
for forbidden in ["url", "credential", "token", "secret", "endpoint", "connection", "private_key"] {
|
||||
assert!(!dto.contains(forbidden), "Program ID autocomplete DTO leaks forbidden marker {forbidden}");
|
||||
}
|
||||
let frontend = read_text(root.join("frontend/ts/main.ts").as_path());
|
||||
for forbidden in ["localStorage", "sessionStorage", "indexedDB", "document.cookie"] {
|
||||
assert!(!frontend.contains(forbidden), "frontend persists campaign/autocomplete material via {forbidden}");
|
||||
}
|
||||
let selection_marker = frontend.find("Backfill Desk address autocomplete selection evaluated");
|
||||
assert!(selection_marker.is_some());
|
||||
let selection_source = match selection_marker {
|
||||
std::option::Option::Some(index) => &frontend[index..std::cmp::min(index + 320, frontend.len())],
|
||||
std::option::Option::None => "",
|
||||
};
|
||||
assert!(selection_source.contains("programCode"));
|
||||
assert!(selection_source.contains("registryMatch"));
|
||||
assert!(!selection_source.contains("address.value"));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/unit_tests/backfill_request.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
fn ready_options() -> crate::BackfillDeskOptionsDto {
|
||||
let limits = crate::backfill_request_limits();
|
||||
@@ -33,6 +33,7 @@ fn ready_options() -> crate::BackfillDeskOptionsDto {
|
||||
],
|
||||
limits,
|
||||
network_coherent: true,
|
||||
program_id_options: crate::program_id_autocomplete_options(),
|
||||
scope_kinds: crate::backfill_scope_kind_codes(),
|
||||
store_diagnostic: std::option::Option::None,
|
||||
store_network: std::option::Option::Some("mainnet-beta".to_owned()),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/unit_tests/dto_common.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
#[test]
|
||||
fn command_error_projection_excludes_arbitrary_context_and_source_values() {
|
||||
@@ -28,6 +28,7 @@ fn transport_options_projection_contains_only_safe_transport_metadata() {
|
||||
composition_ready: true,
|
||||
configured_networks: vec!["devnet".to_owned()],
|
||||
network_coherent: true,
|
||||
program_id_options: crate::program_id_autocomplete_options(),
|
||||
scope_kinds: crate::backfill_scope_kind_codes(),
|
||||
store_diagnostic: std::option::Option::None,
|
||||
store_network: std::option::Option::Some("devnet".to_owned()),
|
||||
@@ -49,3 +50,18 @@ fn transport_options_projection_contains_only_safe_transport_metadata() {
|
||||
assert!(!serialized.contains("database"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn program_id_autocomplete_dataset_is_derived_exactly_from_core_registry() {
|
||||
let options = crate::program_id_autocomplete_options();
|
||||
let entries = ksp_core_lib::entries();
|
||||
assert_eq!(options.len(), entries.len());
|
||||
for (option, entry) in options.iter().zip(entries.iter()) {
|
||||
assert_eq!(option.code, entry.code());
|
||||
assert_eq!(option.name, entry.name());
|
||||
assert_eq!(option.program_id, entry.program_id());
|
||||
assert_eq!(option.domain, entry.domain());
|
||||
assert_eq!(option.family, entry.family());
|
||||
assert_eq!(option.protocol, entry.protocol());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user