наблюдаемость: завершить базовый контур Community
CI / Rust Checks (push) Failing after 4m28s
CI / UI Checks (push) Has been skipped
CI / Frontend E2E (push) Has been skipped
CI / Community Image Smoke (push) Has been skipped
CI / Deploy (push) Has been skipped

Добавить структурированные журналы, метрики, трассировку и безопасный канал критических ошибок. Усилить границы рантайма, тесты, проверку зависимостей и сценарии развёртывания.
This commit is contained in:
2026-07-31 01:01:14 +03:00
parent 99bd05c145
commit 0e8f1ca03a
160 changed files with 13506 additions and 1499 deletions
@@ -38,6 +38,38 @@ fn timestamp(value: &str) -> OffsetDateTime {
OffsetDateTime::parse(value, &Rfc3339).unwrap()
}
#[tokio::test]
async fn bootstrap_user_does_not_overwrite_an_existing_password() {
let database = TestDatabase::new().await;
let registry = database.registry().await;
let email = "bootstrap-owner@example.com";
let user_id = registry
.upsert_bootstrap_user(email, "Bootstrap Owner", "initial-bootstrap-hash")
.await
.unwrap();
registry
.update_user_password(&user_id, "user-selected-hash")
.await
.unwrap();
let repeated_user_id = registry
.upsert_bootstrap_user(email, "Changed Bootstrap Name", "changed-bootstrap-hash")
.await
.unwrap();
let stored = registry
.get_auth_user_by_email(email)
.await
.unwrap()
.unwrap();
assert_eq!(repeated_user_id, user_id);
assert_eq!(stored.password_hash, "user-selected-hash");
assert_eq!(stored.user.display_name, "Bootstrap Owner");
database.cleanup().await;
}
#[tokio::test]
async fn stores_and_finishes_yaml_import_jobs() {
let database = TestDatabase::new().await;
@@ -352,6 +384,24 @@ async fn creates_and_loads_user_sessions_with_typed_expiration() {
assert_eq!(session.user.id, user_id);
assert!(session.user.created_at.unix_timestamp() > 0);
registry.touch_user_session(&session_id).await.unwrap();
let first_seen = sqlx::query_scalar::<_, OffsetDateTime>(
"select last_seen_at from user_sessions where id = $1",
)
.bind(session_id.as_str())
.fetch_one(registry.pool())
.await
.unwrap();
registry.touch_user_session(&session_id).await.unwrap();
let second_seen = sqlx::query_scalar::<_, OffsetDateTime>(
"select last_seen_at from user_sessions where id = $1",
)
.bind(session_id.as_str())
.fetch_one(registry.pool())
.await
.unwrap();
assert_eq!(second_seen, first_seen);
database.cleanup().await;
}
@@ -454,6 +504,40 @@ async fn manages_platform_api_key_read_paths() {
Some(timestamp("2026-03-25T12:05:00Z"))
);
registry
.touch_platform_api_key(
&workspace.id,
&PlatformApiKeyId::new("key_01"),
&timestamp("2026-03-25T12:05:30Z"),
)
.await
.unwrap();
let throttled = registry
.list_platform_api_keys(&workspace.id)
.await
.unwrap();
assert_eq!(
throttled[0].api_key.last_used_at,
Some(timestamp("2026-03-25T12:05:00Z"))
);
registry
.touch_platform_api_key(
&workspace.id,
&PlatformApiKeyId::new("key_01"),
&timestamp("2026-03-25T12:06:01Z"),
)
.await
.unwrap();
let refreshed = registry
.list_platform_api_keys(&workspace.id)
.await
.unwrap();
assert_eq!(
refreshed[0].api_key.last_used_at,
Some(timestamp("2026-03-25T12:06:01Z"))
);
database.cleanup().await;
}
@@ -587,7 +671,6 @@ async fn manages_approval_request_lifecycle() {
.claim_next_recoverable_approval_request(
timestamp("2026-03-25T12:02:01Z"),
timestamp("2026-03-25T12:01:59Z"),
timestamp("2026-03-25T11:55:00Z"),
)
.await
.unwrap();
@@ -608,23 +691,10 @@ async fn manages_approval_request_lifecycle() {
.claim_next_recoverable_approval_request(
timestamp("2026-03-25T12:02:10Z"),
timestamp("2026-03-25T12:02:09Z"),
timestamp("2026-03-25T12:01:59Z"),
)
.await
.unwrap();
assert!(fresh_claim.is_none());
let recovered = registry
.claim_next_recoverable_approval_request(
timestamp("2026-03-25T12:03:00Z"),
timestamp("2026-03-25T12:02:59Z"),
timestamp("2026-03-25T12:02:30Z"),
)
.await
.unwrap()
.unwrap();
assert_eq!(recovered.approval.id, approval.id);
assert_eq!(recovered.approval.status, ApprovalRequestStatus::Executing);
let completed = registry
.finish_approval_request(FinishApprovalRequest {
workspace_id: &workspace_id,
@@ -637,7 +707,6 @@ async fn manages_approval_request_lifecycle() {
.await
.unwrap()
.unwrap();
assert_eq!(completed.approval.status, ApprovalRequestStatus::Completed);
assert_eq!(
completed.approval.response_payload,
@@ -653,9 +722,81 @@ async fn manages_approval_request_lifecycle() {
.await
.unwrap();
assert_eq!(completed_by_status.len(), 1);
let mut interrupted_approval = approval.clone();
interrupted_approval.id = ApprovalRequestId::new("approval_interrupted_01");
interrupted_approval.request_payload = json!({"amount": 150});
interrupted_approval.created_at = timestamp("2026-03-25T12:02:10Z");
registry
.create_approval_request(CreateApprovalRequest {
approval: &interrupted_approval,
})
.await
.unwrap();
registry
.decide_approval_request(DecideApprovalRequest {
workspace_id: &workspace_id,
agent_id: &agent.id,
approval_id: &interrupted_approval.id,
status: ApprovalRequestStatus::Approved,
decided_at: timestamp("2026-03-25T12:02:20Z"),
decided_by_key_id: &approval_key.id,
response_payload: Some(json!({"approve": "yes"})),
decision_note: None,
})
.await
.unwrap()
.unwrap();
registry
.claim_approval_request(
&workspace_id,
&agent.id,
&interrupted_approval.id,
timestamp("2026-03-25T12:02:21Z"),
)
.await
.unwrap()
.unwrap();
let recovered = registry
.claim_next_recoverable_approval_request(
timestamp("2026-03-25T12:03:00Z"),
timestamp("2026-03-25T12:02:59Z"),
)
.await
.unwrap();
assert!(recovered.is_none());
let interrupted = registry
.fail_next_interrupted_approval_request(timestamp("2026-03-25T12:02:30Z"))
.await
.unwrap()
.unwrap();
assert_eq!(interrupted.approval.id, interrupted_approval.id);
assert_eq!(interrupted.approval.status, ApprovalRequestStatus::Failed);
assert_eq!(
completed_by_status[0].approval.status,
ApprovalRequestStatus::Completed
interrupted.approval.response_payload,
Some(json!({
"error": {
"code": "approval_execution_outcome_unknown",
"message": "execution was interrupted; the operation was not retried automatically"
}
}))
);
let failed_by_status = registry
.list_approval_requests(ListApprovalRequestsQuery {
workspace_id: &workspace_id,
status: Some(ApprovalRequestStatus::Failed),
limit: 10,
})
.await
.unwrap();
assert_eq!(failed_by_status.len(), 1);
assert_eq!(
failed_by_status[0].approval.status,
ApprovalRequestStatus::Failed
);
let pending_after_decision = registry