Files
crank/crates/crank-registry/tests/integration/common.rs
T
bsodfather fd8571ad10
CI / Rust Checks (pull_request) Failing after 3s
CI / UI Checks (pull_request) Has been skipped
CI / Frontend E2E (pull_request) Has been skipped
CI / Deployment Manifests (pull_request) Has been skipped
CI / Deploy (pull_request) Has been skipped
Обновить зависимости проекта
2026-07-06 22:47:24 +03:00

280 lines
9.7 KiB
Rust

#![allow(dead_code, unused_imports)]
use std::collections::BTreeMap;
use crank_core::{
AgentId, AgentOperationBinding, AgentStatus, AgentVersion, ApiKeyHeaderAuthConfig, AuthConfig,
AuthKind, AuthProfile, ConfigExport, ExecutionConfig, ExportMode, GeneratedDraft,
GeneratedDraftStatus, HttpMethod, InvocationLog, MembershipRole, OperationId,
OperationSecurityLevel, OperationStatus, PlatformApiKey, PlatformApiKeyId, PlatformApiKeyScope,
PlatformApiKeyStatus, Protocol, RestTarget, RetryPolicy, Samples, SecretId, Target,
ToolDescription, ToolExample, User, UserId, UserSessionId, WizardState, Workspace, WorkspaceId,
};
use crank_mapping::{MappingRule, MappingSet};
use crank_schema::{Schema, SchemaKind};
use serde_json::json;
use sqlx::{Executor, PgPool, postgres::PgPoolOptions};
use time::{OffsetDateTime, format_description::well_known::Rfc3339};
use crank_registry::{
CreateAgentRequest, CreateInvocationLogRequest, CreatePlatformApiKeyRequest,
CreateVersionRequest, CreateWorkspaceRequest, CreateYamlImportJobRequest, DescriptorKind,
DescriptorMetadata, OperationSampleMetadata, PlatformApiKeyRecord, PostgresRegistry,
PublishAgentRequest, PublishRequest, RegistryError, RegistryOperation, SampleKind,
SaveAuthProfileRequest, SaveDescriptorMetadataRequest, SaveSampleMetadataRequest,
WorkspaceRecord, YamlImportJobCompletion, YamlImportJobId, YamlImportJobStatus,
};
fn test_workspace_id() -> WorkspaceId {
WorkspaceId::new("ws_default")
}
fn timestamp(value: &str) -> OffsetDateTime {
OffsetDateTime::parse(value, &Rfc3339).unwrap()
}
pub(super) fn test_operation(id: &str, version: u32, status: OperationStatus) -> RegistryOperation {
RegistryOperation {
id: OperationId::new(id),
name: format!("{id}_tool"),
display_name: format!("Display {id}"),
category: "general".to_owned(),
protocol: Protocol::Rest,
security_level: OperationSecurityLevel::Standard,
status,
version,
target: Target::Rest(RestTarget {
base_url: "https://api.example.com".to_owned(),
method: HttpMethod::Post,
path_template: "/v1/leads".to_owned(),
static_headers: BTreeMap::from([("X-Static".to_owned(), "true".to_owned())]),
}),
input_schema: Schema {
kind: SchemaKind::Object,
description: Some("input".to_owned()),
required: true,
nullable: false,
default_value: None,
fields: BTreeMap::from([(
"email".to_owned(),
Schema {
kind: SchemaKind::String,
description: None,
required: true,
nullable: false,
default_value: None,
fields: BTreeMap::new(),
items: None,
enum_values: Vec::new(),
variants: Vec::new(),
},
)]),
items: None,
enum_values: Vec::new(),
variants: Vec::new(),
},
output_schema: Schema {
kind: SchemaKind::Object,
description: Some("output".to_owned()),
required: true,
nullable: false,
default_value: None,
fields: BTreeMap::from([(
"id".to_owned(),
Schema {
kind: SchemaKind::String,
description: None,
required: true,
nullable: false,
default_value: None,
fields: BTreeMap::new(),
items: None,
enum_values: Vec::new(),
variants: Vec::new(),
},
)]),
items: None,
enum_values: Vec::new(),
variants: Vec::new(),
},
input_mapping: MappingSet {
rules: vec![MappingRule {
source: "$.mcp.email".to_owned(),
target: "$.request.body.email".to_owned(),
required: true,
default_value: None,
transform: None,
condition: None,
notes: None,
}],
},
output_mapping: MappingSet {
rules: vec![MappingRule {
source: "$.response.body.id".to_owned(),
target: "$.output.id".to_owned(),
required: true,
default_value: None,
transform: None,
condition: None,
notes: None,
}],
},
execution_config: ExecutionConfig {
timeout_ms: 10_000,
retry_policy: Some(RetryPolicy { max_attempts: 3 }),
auth_profile_ref: Some("auth_crank".into()),
headers: BTreeMap::from([("X-Request-Id".to_owned(), "static".to_owned())]),
..ExecutionConfig::default()
},
tool_description: ToolDescription {
title: "Create lead".to_owned(),
description: "Creates CRM lead".to_owned(),
tags: vec!["crm".to_owned()],
examples: vec![ToolExample {
input: json!({ "email": "a@example.com" }),
}],
},
samples: Some(Samples {
input_json_sample_ref: Some("sample_input".into()),
output_json_sample_ref: Some("sample_output".into()),
}),
generated_draft: Some(GeneratedDraft {
status: GeneratedDraftStatus::Available,
source_types: vec!["input_json".to_owned(), "output_json".to_owned()],
generated_at: Some("2026-03-25T11:59:00Z".to_owned()),
input_schema_generated: true,
output_schema_generated: true,
input_mapping_generated: true,
output_mapping_generated: true,
warnings: Vec::new(),
}),
config_export: Some(ConfigExport {
format_version: "v1".to_owned(),
export_mode: ExportMode::Portable,
}),
wizard_state: Some(WizardState {
input_sample: Some(json!({ "email": format!("lead-{version}@example.com") })),
output_sample: Some(json!({ "id": format!("lead_{version}") })),
test_input: Some(json!({ "email": format!("test-v{version}@example.com") })),
import_findings: Vec::new(),
}),
created_at: timestamp("2026-03-25T11:58:00Z"),
updated_at: timestamp(&format!("2026-03-25T12:{version:02}:00Z")),
published_at: None,
}
}
pub(super) fn test_agent(id: &str, status: AgentStatus) -> crank_core::Agent {
crank_core::Agent {
id: AgentId::new(id),
workspace_id: test_workspace_id(),
slug: format!("{id}_slug"),
display_name: format!("Display {id}"),
description: format!("Description {id}"),
status,
current_draft_version: 1,
latest_published_version: None,
created_at: timestamp("2026-03-25T11:58:00Z"),
updated_at: timestamp("2026-03-25T12:00:00Z"),
published_at: None,
}
}
pub(super) fn test_agent_version(
agent_id: &AgentId,
version: u32,
status: AgentStatus,
) -> AgentVersion {
AgentVersion {
agent_id: agent_id.clone(),
version,
status,
instructions: json!({
"system": "triage tickets",
"guardrails": ["don't mutate state"]
}),
tool_selection_policy: json!({
"mode": "allow_list",
"max_tools": 8
}),
created_at: timestamp("2026-03-25T12:00:00Z"),
}
}
pub(super) fn test_invocation_log(
id: &str,
operation_id: &OperationId,
agent_id: Option<AgentId>,
status: crank_core::InvocationStatus,
duration_ms: u64,
created_at: &str,
) -> InvocationLog {
InvocationLog {
id: crank_core::InvocationLogId::new(id),
workspace_id: test_workspace_id(),
agent_id,
operation_id: operation_id.clone(),
source: crank_core::InvocationSource::AgentToolCall,
level: crank_core::InvocationLevel::Info,
status,
tool_name: "create_lead".to_owned(),
message: "invocation".to_owned(),
request_id: Some(format!("req_{id}")),
status_code: Some(200),
duration_ms,
error_kind: None,
request_preview: json!({"input":"value"}),
response_preview: json!({"ok":true}),
created_at: timestamp(created_at),
}
}
pub(super) struct TestDatabase {
admin_pool: PgPool,
database_url: String,
schema: String,
}
impl TestDatabase {
pub(super) async fn new() -> Self {
let database_url = crank_test_support::postgres_database_url().await.to_owned();
let admin_pool = PgPoolOptions::new()
.max_connections(1)
.connect(&database_url)
.await
.unwrap();
let schema = crank_test_support::unique_schema_name("test_registry");
admin_pool
.execute(sqlx::query(sqlx::AssertSqlSafe(format!(
"create schema {schema}"
))))
.await
.unwrap();
Self {
admin_pool,
database_url,
schema,
}
}
pub(super) async fn registry(&self) -> PostgresRegistry {
PostgresRegistry::connect(&format!(
"{}?options=-csearch_path%3D{}",
self.database_url, self.schema
))
.await
.unwrap()
}
pub(super) async fn cleanup(&self) {
self.admin_pool
.execute(sqlx::query(sqlx::AssertSqlSafe(format!(
"drop schema if exists {} cascade",
self.schema
))))
.await
.unwrap();
}
}