quvyta-cli 0.1.0-alpha.5

A small coding agent in the terminal: chat with a model that reads, searches, edits and runs in one folder, and asks before it changes anything (alpha)
Documentation
//! A chat opened in a QCode profile: the profile's relay and model are the provider, and the
//! settings file is not asked.

use super::tests::{Place, harness, say};
use super::*;
use crate::config::{ADDRESS_VARIABLE, MODEL_VARIABLE, ProfileError, UpdateFolders};
use crate::provider::fake::{FakeServer, says};

/// The environment a profile gives: the relay's address and the model the profile chose.
fn profile(address: &str) -> impl Fn(&str) -> Option<String> + '_ {
    move |name| match name {
        ADDRESS_VARIABLE => Some(address.to_owned()),
        MODEL_VARIABLE => Some("profile-model-3".to_owned()),
        _ => None,
    }
}

fn profile_app(place: &Place, address: &str) -> QCli {
    opening(
        Workspace::new(&place.work()).expect("workspace"),
        place.store(),
        place.settings(None),
        profile(address),
        None,
    )
    .expect("the profile opens")
}

#[test]
fn a_profile_tab_talks_to_the_relay_with_the_profiles_model_and_no_key() {
    let place = Place::new("profile-chat");
    let server = FakeServer::start(vec![(200, says("from the relay"))]);
    let mut harness = harness(profile_app(&place, &server.address));
    assert!(harness.app().form.is_none(), "no provider form opens");
    assert!(!harness.screen().contains("Key file"), "no form field is on screen: {}", harness.screen());
    assert!(harness.screen().contains("profile-model-3"), "the top bar shows the model: {}", harness.screen());

    say(&mut harness, "hello");
    assert!(harness.screen().contains("from the relay"), "{}", harness.screen());

    let received = server.received();
    assert_eq!(received.len(), 1, "exactly one request");
    assert_eq!(received[0].body["model"], "profile-model-3");
    for header in ["x-api-key", "api-key", "authorization"] {
        assert_eq!(received[0].header(header), None, "no {header} header goes with a profile's request");
    }
}

#[test]
fn a_profile_wins_over_the_settings_file() {
    let place = Place::new("profile-wins");
    let server = FakeServer::start(vec![(200, says("from the relay"))]);
    let other = FakeServer::start(vec![(200, says("from the settings"))]);
    let app = opening(
        Workspace::new(&place.work()).expect("workspace"),
        place.store(),
        place.settings(Some(&other.address)),
        profile(&server.address),
        None,
    )
    .expect("the profile opens");
    let mut harness = harness(app);
    say(&mut harness, "hello");

    assert!(harness.screen().contains("from the relay"), "{}", harness.screen());
    assert_eq!(server.received().len(), 1, "the profile's relay was asked");
    assert_eq!(other.received().len(), 0, "the settings file's provider was not");
}

#[test]
fn ctrl_o_in_a_profile_tab_says_where_the_provider_is_changed_and_opens_no_form() {
    let place = Place::new("profile-ctrl-o");
    let server = FakeServer::start(vec![]);
    let mut harness = harness(profile_app(&place, &server.address));
    harness.press("ctrl+o");

    assert!(
        harness.screen().contains("This tab's provider comes from its QCode profile"),
        "the person is told where it is changed: {}",
        harness.screen()
    );
    assert!(!harness.screen().contains("Key file"), "no form field is on screen: {}", harness.screen());
    assert!(harness.app().form.is_none(), "no form opened");
    let settings = place.base().join("config").join("cli.conf");
    let written = std::fs::read_to_string(&settings).unwrap_or_default();
    assert!(!written.contains("profile-model-3"), "the profile's model is not written: {written}");
}

#[test]
fn a_profile_tab_does_not_ask_about_updates_and_a_plain_one_does() {
    let place = Place::new("profile-updates");
    let folders = || UpdateFolders { config: place.base().join("config"), state: place.base().join("state") };
    let server = FakeServer::start(vec![]);

    let in_profile = opening(
        Workspace::new(&place.work()).expect("workspace"),
        place.store(),
        place.settings(None),
        profile(&server.address),
        Some(folders()),
    )
    .expect("the profile opens");
    assert!(in_profile.updates.is_none(), "a profile's tab asks for no update");

    let plain = opening(
        Workspace::new(&place.work()).expect("workspace"),
        place.store(),
        place.settings(None),
        |_: &str| None,
        Some(folders()),
    )
    .expect("the plain start opens");
    assert!(plain.updates.is_some(), "a tab that is not in a profile keeps the update notice");
}

#[test]
fn opening_with_only_one_variable_is_an_error() {
    let place = Place::new("profile-one");
    let only_address = |name: &str| (name == ADDRESS_VARIABLE).then(|| "http://127.0.0.1:41417".to_owned());
    let result = opening(
        Workspace::new(&place.work()).expect("workspace"),
        place.store(),
        place.settings(None),
        only_address,
        None,
    );
    let Err(error) = result else { panic!("one variable is an error") };
    assert_eq!(error, ProfileError::Missing(MODEL_VARIABLE));
}