use super::super::ShellState;
use crate::AppModel;
use crate::app::Roots;
use crate::cli_outbound::Cli;
use crate::git_tree::tests::fixture::Fixture;
use crate::model_pick::PROVIDERS;
use crate::projects::runner::BlStore;
use crate::ui_state::SystemClock;
use crate::xdg::Env;
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
use std::sync::Arc;
use tempfile::tempdir;
pub(super) const MINT_SEED: u64 = 0xc0df;
pub(super) const MINTED: [&str; 3] = ["CourtyardRooftop", "AxolotlHeadland", "XylophoneAzure"];
pub(super) const MINTED_FIRST: &str = MINTED[0];
pub(super) fn fake_lernie(dir: &Path) -> Cli {
let path = dir.join("lernie");
std::fs::write(
&path,
format!(
"#!/bin/sh\ncase \"$1\" in\n{}esac\nexit 0\n",
crate::test_support::authoring_new_arm()
),
)
.unwrap();
let mut perms = std::fs::metadata(&path).unwrap().permissions();
perms.set_mode(0o755);
std::fs::set_permissions(&path, perms).unwrap();
Cli::new(path)
}
pub(super) fn seed_world(world: &World) {
let lernie_home = crate::world::layout_under(&world.yog_data).lernie;
std::fs::create_dir_all(&lernie_home).unwrap();
std::fs::write(lernie_home.join("models.yaml"), b"models: {}\n").unwrap();
}
pub(in crate::shell::acceptance) mod crowd;
pub(in crate::shell::acceptance) mod wide;
mod follow;
mod world;
pub(in crate::shell::acceptance) use world::World;
#[derive(PartialEq, Eq)]
enum Roster {
One,
Empty,
Crowded,
}
pub(super) fn world_titled(title: &str) -> World {
build_world(title, &Roster::One)
}
pub(super) fn world() -> World {
build_world("hello", &Roster::One)
}
pub(super) fn world_empty() -> World {
build_world("hello", &Roster::Empty)
}
pub(super) fn world_crowded() -> World {
build_world("hello", &Roster::Crowded)
}
fn build_world(title: &str, roster: &Roster) -> World {
let root = tempdir().unwrap();
let yog_data = root.path().join("yog");
let roots = Roots {
yog_data: yog_data.clone(),
lernie_data: root.path().join("lernie"),
yog_state: root.path().join("state"),
balls_clones: root.path().join("balls").join("clones"),
home: root.path().join("home"),
world: crate::test_support::world_under(root.path()),
};
std::fs::create_dir_all(roots.lernie_data.join("workspaces")).unwrap();
std::fs::write(
roots.lernie_data.join("models.yaml"),
b"models:\n m:\n provider: anthropic\n model_id: m\n context_window: 200000\n",
)
.unwrap();
std::fs::create_dir_all(&roots.yog_state).unwrap();
let fx = Fixture::new();
fx.commit_other(PROVIDERS, crate::test_support::TEMPLATE_PROVIDERS);
fx.build_agent("c-1", title);
if *roster == Roster::Crowded {
crowd::seat(&fx);
}
let messages = fx.path.join("agents/c-1/messages");
std::fs::create_dir_all(&messages).unwrap();
std::fs::write(messages.join("001-user.md"), "please ping").unwrap();
std::fs::write(
messages.join("002-opus.json"),
br#"{"content":[{"type":"text","text":"pong reply"}]}"#,
)
.unwrap();
let step = fx.path.join("steps/c-1/001");
std::fs::create_dir_all(step.join("tools/toolu_1")).unwrap();
std::fs::write(
step.join("response.json"),
b"{\"type\":\"usage\",\"input_tokens\":10,\"output_tokens\":5,\"cache_read_tokens\":50000}\n{\"type\":\"error\",\"kind\":\"auth\",\"message\":\"no credential for this provider\",\"provider_detail\":null}\n{\"type\":\"end\"}\n",
)
.unwrap();
std::fs::write(step.join("tools/toolu_1/input.json"), br#"{"name":"Read"}"#).unwrap();
std::fs::write(
step.join("tools/toolu_1/output.json"),
br#"{"exit_code":0}"#,
)
.unwrap();
fx.deposit_message(
"c-1",
"user-001.md",
"---\nfrom: user\ndeposited_at: t0\n---\nfollow-up message",
);
let lernie_workspaces = roots.lernie_data.join("workspaces");
let names_root = crate::binding::names_root(&yog_data);
let ws = if *roster == Roster::Crowded {
std::fs::create_dir_all(&names_root).unwrap();
names_root.join(crowd::WALL)
} else {
lernie_workspaces.join("ws")
};
if *roster != Roster::Empty {
std::os::unix::fs::symlink(&fx.path, &ws).unwrap();
}
let env = Env::from_pairs([("HOME", root.path().display().to_string())]);
let balls = Box::new(BlStore::new(env.balls_layout(), Cli::new("bl")));
let (mut model, deriver) = AppModel::boot(
roots,
(*roster != Roster::Empty).then(|| ws.clone()),
Arc::new(SystemClock),
balls,
Some("me".into()),
);
let mut state = ShellState::new(&env, Arc::new(SystemClock)).unwrap();
state.start.mint_seed = MINT_SEED;
let (link, link_end) = crate::wire::link::pair();
let (post, outbox) = crate::wire::post::pair();
let (tail, tail_end) = crate::wire::lane::pair();
model.adopt_wire(link);
model.adopt_post(post);
model.adopt_tail(tail);
World {
_root: root,
fx,
spheres: Vec::new(),
model,
deriver,
state,
ws,
yog_data,
lernie_workspaces,
link: link_end,
outbox,
tail: tail_end,
followed: None,
lernie: Cli::new("/yog-absent-lernie"),
bl: Cli::new("/yog-absent-bl"),
}
}