asa-api / Tests /api_contract_smoke.R
cjerzak's picture
add files
f23dbdb
suppressPackageStartupMessages({
library(asa)
})
args <- commandArgs(trailingOnly = FALSE)
file_arg <- "--file="
script_arg <- args[grepl(paste0("^", file_arg), args)]
script_path <- if (length(script_arg)) {
normalizePath(sub(file_arg, "", script_arg[[1]]), mustWork = TRUE)
} else {
normalizePath("Tests/api_contract_smoke.R", mustWork = TRUE)
}
repo_root <- normalizePath(file.path(dirname(script_path), ".."), mustWork = TRUE)
source(file.path(repo_root, "R", "asa_api_helpers.R"))
assert_true <- function(condition, message) {
if (!isTRUE(condition)) {
stop(message, call. = FALSE)
}
}
expect_error_contains <- function(expr, pattern) {
error_message <- NULL
tryCatch(
force(expr),
error = function(e) {
error_message <<- conditionMessage(e)
NULL
}
)
if (is.null(error_message)) {
stop(sprintf("Expected error containing %s, but no error was raised.", sQuote(pattern)), call. = FALSE)
}
if (!grepl(pattern, error_message, fixed = TRUE)) {
stop(
sprintf(
"Expected error containing %s, got: %s",
sQuote(pattern),
error_message
),
call. = FALSE
)
}
invisible(error_message)
}
prompts <- asa_api_require_prompts(list(prompts = list(" Hello ", "World ")))
assert_true(identical(prompts, c("Hello", "World")), "Valid prompt arrays should be trimmed and preserved.")
expect_error_contains(
asa_api_require_prompts(list(prompts = list(list(prompt = "Q1", id = "row1")))),
"Structured prompt objects are not supported by `/v1/batch`."
)
expect_error_contains(
asa_api_require_prompts(list(prompts = list("Hello", TRUE))),
"`prompts` must be a JSON array of non-empty strings."
)
expect_error_contains(
asa_api_require_prompts(list(prompts = list("Hello", " "))),
"Each entry in `prompts` must be a non-empty string."
)
asa_api_validate_batch_supported_fields(list(
prompts = list("Hello", "World"),
run = list(output_format = "text", performance_profile = "balanced")
))
expect_error_contains(
asa_api_validate_batch_supported_fields(list(
prompts = list("Hello"),
run = list(expected_schema = list(type = "object"))
)),
"Unsupported `/v1/batch` `run` keys:"
)
expect_error_contains(
asa_api_validate_batch_supported_fields(list(
prompts = list("Hello"),
use_plan_mode = TRUE
)),
"Unsupported `/v1/batch` top-level keys:"
)
run_args <- asa_api_build_run_args(list(run = list(
expected_schema = list(type = "object"),
use_plan_mode = TRUE,
performance_profile = "balanced"
)))
assert_true(
all(c("output_format", "expected_schema", "use_plan_mode", "performance_profile") %in% names(run_args)),
"`/v1/run` should continue forwarding newer upstream run_task options."
)
batch_args <- asa_api_build_batch_args(list(
run = list(output_format = "json", performance_profile = "quality"),
parallel = FALSE
))
assert_true(
all(c("output_format", "performance_profile", "parallel", "progress") %in% names(batch_args)),
"Batch-compatible shared options should still flow into run_task_batch."
)
assert_true(
identical(asa_api_has_run_direct_task(), !is.null(asa_api_get_run_direct_task(optional = TRUE))),
"Direct-provider capability checks should agree on run_direct_task availability."
)
mock_request <- function(path = "/v1/run", authorization = NULL, x_api_key = NULL) {
headers <- list()
if (!is.null(authorization)) {
headers$authorization <- authorization
}
if (!is.null(x_api_key)) {
headers[["x-api-key"]] <- x_api_key
}
list(
PATH_INFO = path,
HEADERS = headers
)
}
assert_true(
isTRUE(asa_api_path_requires_bearer_auth("/v1/run")) &&
isTRUE(asa_api_path_requires_bearer_auth("/v1/batch")),
"`/v1/*` routes should require bearer auth."
)
assert_true(
!isTRUE(asa_api_path_requires_bearer_auth("/healthz")) &&
!isTRUE(asa_api_path_requires_bearer_auth("/gui/query")),
"Health and GUI routes should remain outside bearer auth scope."
)
assert_true(
identical(asa_api_required_bearer_token(), "999"),
"Bearer auth should require the fixed token 999."
)
assert_true(
identical(
asa_api_extract_bearer_token(mock_request(authorization = "Bearer 999")),
"999"
),
"Bearer extraction should accept the required token."
)
assert_true(
identical(
asa_api_extract_bearer_token(list(
PATH_INFO = "/v1/run",
HEADERS = list(Authorization = "Bearer 999")
)),
"999"
),
"Bearer extraction should match Authorization headers case-insensitively."
)
assert_true(
identical(
asa_api_extract_bearer_token(mock_request(authorization = "Basic 999")),
""
),
"Non-bearer Authorization schemes should not be accepted."
)
assert_true(
isTRUE(asa_api_has_required_bearer_token(mock_request(authorization = "Bearer 999"))),
"Bearer auth should accept Authorization: Bearer 999."
)
assert_true(
!isTRUE(asa_api_has_required_bearer_token(mock_request(authorization = "Bearer wrong"))),
"Bearer auth should reject the wrong token."
)
assert_true(
!isTRUE(asa_api_has_required_bearer_token(mock_request(x_api_key = "999"))),
"Legacy x-api-key auth should no longer be accepted."
)
health_payload <- asa_api_health_payload()
assert_true(
identical(isTRUE(health_payload$direct_provider_available), isTRUE(asa_api_has_run_direct_task())),
"Health payload should report direct-provider availability from the same capability check."
)
if (isTRUE(health_payload$direct_provider_available)) {
assert_true(
is.null(health_payload$direct_provider_note),
"Health payload should omit the direct-provider note when the capability is available."
)
} else {
assert_true(
is.character(health_payload$direct_provider_note) && nzchar(trimws(health_payload$direct_provider_note)),
"Health payload should explain why direct-provider mode is unavailable."
)
}
{
original_asa <- asa_api_run_single_via_asa
original_direct <- asa_api_run_single_via_direct
dispatch_calls <- character(0)
asa_api_run_single_via_asa <- function(payload) {
dispatch_calls <<- c(dispatch_calls, "asa")
list(status = "success", execution = list(mode = "asa_agent"))
}
asa_api_run_single_via_direct <- function(payload) {
dispatch_calls <<- c(dispatch_calls, "direct")
list(status = "success", execution = list(mode = "provider_direct"))
}
asa_api_run_single(
list(prompt = "Hello", use_direct_provider = TRUE),
allow_direct_provider = FALSE
)
assert_true(
identical(dispatch_calls, "asa"),
"Single-run API should ignore the private direct-provider flag."
)
dispatch_calls <- character(0)
gui_direct <- asa_api_run_single(
list(prompt = "Hello", use_direct_provider = TRUE),
allow_direct_provider = TRUE
)
assert_true(
identical(dispatch_calls, "direct"),
"GUI single-run path should dispatch to direct-provider mode when enabled."
)
assert_true(
identical(gui_direct$execution$mode, "provider_direct"),
"Direct-provider dispatch should preserve provider_direct mode metadata."
)
asa_api_run_single_via_asa <- original_asa
asa_api_run_single_via_direct <- original_direct
}
gui_result <- asa_api_sanitize_gui_result(list(
status = "success",
execution = list(
mode = "provider_direct",
config_snapshot = list(backend = "gemini", model = "gemini-3-flash-preview"),
payload_integrity = list(
backend = "gemini",
model = "gemini-3-flash-preview",
released_from = "message_text"
),
trace_metadata = list(
structured_output_backend = "gemini",
model_name = "gemini-3-flash-preview"
)
)
))
assert_true(
is.null(gui_result$execution$config_snapshot$backend) &&
is.null(gui_result$execution$config_snapshot$model),
"GUI responses should not expose backend/model selection in execution metadata."
)
assert_true(
is.null(gui_result$execution$payload_integrity$backend) &&
is.null(gui_result$execution$payload_integrity$model),
"GUI payload integrity metadata should hide backend/model fields."
)
cat("asa-api contract smoke checks passed\n")