Skip to main content

dropshot_api_manager/
test_util.rs

1// Copyright 2026 Oxide Computer Company
2
3//! Test utilities for the Dropshot API manager.
4
5pub use crate::output::CheckResult;
6#[doc(hidden)]
7pub use crate::resolved::{ProblemKind, ProblemSummary};
8use crate::{
9    apis::ManagedApis,
10    cmd::{
11        check::check_impl_with_summaries,
12        dispatch::{BlessedSourceArgs, GeneratedSourceArgs},
13    },
14    environment::{Environment, GeneratedSource},
15    output::{OutputOpts, Styles},
16    resolved,
17};
18use anyhow::Context;
19use camino::Utf8PathBuf;
20
21/// Check that a set of APIs is up-to-date.
22///
23/// This is meant to be called within a test.
24pub fn check_apis_up_to_date(
25    env: &Environment,
26    apis: &ManagedApis,
27) -> Result<CheckResult, anyhow::Error> {
28    let (result, _summaries) = check_apis_with_summaries(env, apis)?;
29    Ok(result)
30}
31
32/// Check that a set of APIs is up-to-date, loading generated documents from
33/// the given directory instead of generating them from the API definitions.
34pub fn check_apis_with_generated_from_dir(
35    env: &Environment,
36    apis: &ManagedApis,
37    generated_from_dir: Utf8PathBuf,
38) -> Result<CheckResult, anyhow::Error> {
39    let (result, _summaries) =
40        check_apis_with_generated_from_dir_and_summaries(
41            env,
42            apis,
43            generated_from_dir,
44        )?;
45    Ok(result)
46}
47
48/// Like [`check_apis_up_to_date`], but also returns the list of problem
49/// summaries for detailed assertions in tests.
50///
51/// The rendered output is written to stderr. To capture the rendered output
52/// into a `String` instead, use [`check_apis_with_render`].
53#[doc(hidden)]
54pub fn check_apis_with_summaries(
55    env: &Environment,
56    apis: &ManagedApis,
57) -> Result<(CheckResult, Vec<resolved::ProblemSummary>), anyhow::Error> {
58    let env = resolve_env(env)?;
59    let (blessed_source, generated_source, output) =
60        default_sources(&env, None)?;
61    let styles = output.styles(supports_color::Stream::Stderr);
62    check_impl_with_summaries(
63        &mut std::io::stderr().lock(),
64        apis,
65        &env,
66        &blessed_source,
67        &generated_source,
68        &styles,
69    )
70}
71
72/// Like [`check_apis_with_generated_from_dir`], but also returns the list
73/// of problem summaries for detailed assertions in tests.
74#[doc(hidden)]
75pub fn check_apis_with_generated_from_dir_and_summaries(
76    env: &Environment,
77    apis: &ManagedApis,
78    generated_from_dir: Utf8PathBuf,
79) -> Result<(CheckResult, Vec<resolved::ProblemSummary>), anyhow::Error> {
80    let env = resolve_env(env)?;
81    let (blessed_source, generated_source, output) =
82        default_sources(&env, Some(generated_from_dir))?;
83    let styles = output.styles(supports_color::Stream::Stderr);
84    check_impl_with_summaries(
85        &mut std::io::stderr().lock(),
86        apis,
87        &env,
88        &blessed_source,
89        &generated_source,
90        &styles,
91    )
92}
93
94/// Like [`check_apis_with_summaries`], but captures the rendered check output
95/// into a `String` rather than writing it to stderr. Styling is disabled.
96#[doc(hidden)]
97pub fn check_apis_with_render(
98    env: &Environment,
99    apis: &ManagedApis,
100) -> Result<(CheckResult, Vec<resolved::ProblemSummary>, String), anyhow::Error>
101{
102    let env = resolve_env(env)?;
103    let (blessed_source, generated_source, _output) =
104        default_sources(&env, None)?;
105    // Force defaults (no color) so the captured snapshot is terminal-agnostic.
106    let styles = Styles::default();
107    let mut buf: Vec<u8> = Vec::new();
108    let (result, summaries) = check_impl_with_summaries(
109        &mut buf,
110        apis,
111        &env,
112        &blessed_source,
113        &generated_source,
114        &styles,
115    )?;
116    let rendered = String::from_utf8(buf)
117        .context("rendered output should be valid UTF-8")?;
118    Ok((result, summaries, rendered))
119}
120
121fn resolve_env(
122    env: &Environment,
123) -> Result<crate::environment::ResolvedEnv, anyhow::Error> {
124    // env.resolve(None) assumes that env.default_openapi_dir is where the
125    // OpenAPI documents live and doesn't need a further override. (If a custom
126    // directory is desired, it can always be passed in via `env`.)
127    env.resolve(None)
128}
129
130fn default_sources(
131    env: &crate::environment::ResolvedEnv,
132    generated_from_dir: Option<Utf8PathBuf>,
133) -> Result<
134    (crate::environment::BlessedSource, GeneratedSource, OutputOpts),
135    anyhow::Error,
136> {
137    let blessed_source = BlessedSourceArgs {
138        blessed_from_vcs: None,
139        blessed_from_vcs_path: None,
140        blessed_from_dir: None,
141    }
142    .to_blessed_source(env)?;
143    let generated_source =
144        GeneratedSource::from(GeneratedSourceArgs { generated_from_dir });
145    let output = OutputOpts { color: clap::ColorChoice::Auto };
146    Ok((blessed_source, generated_source, output))
147}