use std::path::Path;
use super::fixture::{World, world};
use super::screen::{Screen, click, locate};
use crate::cli_outbound::Cli;
use crate::keymap::CenterTab;
const DRAFT_A: &str = "yog-5894-unsaved-draft";
const RAW_HEADER: &str = "raw config.toml";
const PICKER_SCOPE: &str = "workspace default too";
fn fake_bz(dir: &Path, line: &str) -> Cli {
use std::os::unix::fs::PermissionsExt;
let path = dir.join("bz-login");
std::fs::create_dir_all(dir).unwrap();
std::fs::write(&path, format!("#!/bin/sh\necho '{line}' >&2\nexit 0\n")).unwrap();
let mut perms = std::fs::metadata(&path).unwrap().permissions();
perms.set_mode(0o755);
std::fs::set_permissions(&path, perms).unwrap();
Cli::new(path)
}
fn open_raw_editor(screen: &Screen, world: &mut World) {
let shapes = screen.shapes(world, Vec::new());
let header = locate(&shapes, RAW_HEADER).expect("the raw editor's fold header");
click(screen, world, header);
}
fn two_spheres(screen: &Screen) -> (World, std::path::PathBuf, std::path::PathBuf) {
let mut world = world();
let a = world.ws.clone();
let b = world.add_workspace("other", "c-2");
world.converge();
world.model.focus_workspace(&crate::naming::leaf(&a));
screen.idle(&mut world);
(world, a, b)
}
fn settle_for(screen: &Screen, world: &mut World, needle: &str) -> String {
let mut out = String::new();
for _ in 0..200 {
out = screen.text(world);
if out.contains(needle) {
return out;
}
std::thread::sleep(std::time::Duration::from_millis(5));
}
out
}
#[test]
fn an_unsaved_brazen_draft_survives_a_workspace_round_trip() {
let screen = Screen::new();
let (mut world, a, b) = two_spheres(&screen);
crate::shell::center::focus(&world.model, &mut world.state, CenterTab::Config);
world
.state
.wall
.brazen
.editor
.as_mut()
.expect("workspace A is a wall, so it has a config.toml to edit")
.draft_mut()
.push_str(DRAFT_A);
open_raw_editor(&screen, &mut world);
let under_a = screen.text(&mut world);
assert!(
under_a.contains(DRAFT_A),
"the draft is in workspace A's box:\n{under_a}"
);
world.model.focus_workspace(&crate::naming::leaf(&b));
let under_b = screen.text(&mut world);
assert!(
under_b.contains("brazen config.toml"),
"B has a brazen pane of its own:\n{under_b}"
);
assert!(
!under_b.contains(DRAFT_A),
"and A's draft is not in it — nothing crosses a wall:\n{under_b}"
);
world.model.focus_workspace(&crate::naming::leaf(&a));
let back = screen.text(&mut world);
assert!(
back.contains(DRAFT_A),
"returning to A restores the draft it was left with:\n{back}"
);
}
#[test]
fn a_sign_in_stream_paints_only_under_the_wall_that_started_it() {
let screen = Screen::new();
let (mut world, a, b) = two_spheres(&screen);
crate::shell::center::focus(&world.model, &mut world.state, CenterTab::Login);
let line = "yog-5894-authorize-url";
let bz = fake_bz(&world.yog_data, line);
let run = crate::login::start(
&bz,
"openai-chatgpt",
world.model.state_root(),
"t0",
Some(&a),
)
.expect("the fake bz spawns");
world.state.wall.login.run = Some(run);
let under_a = settle_for(&screen, &mut world, line);
assert!(
under_a.contains(line),
"A's sign-in prints where it was started:\n{under_a}"
);
world.model.focus_workspace(&crate::naming::leaf(&b));
let under_b = screen.text(&mut world);
assert!(
under_b.contains("Login (bz browser sign-in)"),
"B has a Login surface of its own:\n{under_b}"
);
assert!(
!under_b.contains(line),
"but A's authorize line is not on it — a device code is for one sphere:\n{under_b}"
);
world.model.focus_workspace(&crate::naming::leaf(&a));
let back = screen.text(&mut world);
assert!(
back.contains(line),
"and the run was parked with A, not killed by leaving it:\n{back}"
);
}
#[test]
fn a_picker_opened_in_one_wall_is_not_open_in_the_next() {
let screen = Screen::new();
let (mut world, a, b) = two_spheres(&screen);
world.state.wall.picker.open = true;
let under_a = settle_for(&screen, &mut world, PICKER_SCOPE);
assert!(
under_a.contains(PICKER_SCOPE),
"the picker pane is open on A:\n{under_a}"
);
world.model.focus_workspace(&crate::naming::leaf(&b));
world.converge();
let under_b = settle_for(&screen, &mut world, "work directory:");
assert!(
!world.state.wall.picker.open,
"B's picker is B's own, and it was never opened"
);
assert!(
!under_b.contains(PICKER_SCOPE),
"so no write surface from A paints over B:\n{under_b}"
);
assert!(
!under_b.contains(crate::model_pick::WRITE_NOTE),
"and none of what a pick would write:\n{under_b}"
);
world.model.focus_workspace(&crate::naming::leaf(&a));
world.converge();
let back = settle_for(&screen, &mut world, PICKER_SCOPE);
assert!(
world.state.wall.picker.open,
"and A's picker is still where A left it"
);
assert!(back.contains(PICKER_SCOPE), "on screen again:\n{back}");
}
#[test]
fn unmaking_a_workspace_unmakes_its_ram_too() {
let screen = Screen::new();
let (mut world, a, b) = two_spheres(&screen);
crate::shell::center::focus(&world.model, &mut world.state, CenterTab::Config);
world
.state
.wall
.brazen
.editor
.as_mut()
.expect("workspace A is a wall")
.draft_mut()
.push_str(DRAFT_A);
open_raw_editor(&screen, &mut world);
let typed = screen.text(&mut world);
assert!(
typed.contains(DRAFT_A),
"the draft is on screen before the sphere is unmade:\n{typed}"
);
world.state.forget_wall(&a);
world.model.focus_workspace(&crate::naming::leaf(&b));
screen.idle(&mut world);
world.model.focus_workspace(&crate::naming::leaf(&a));
let reborn = screen.text(&mut world);
assert!(
!reborn.contains(DRAFT_A),
"a dead sphere's draft is not handed to the path's next occupant:\n{reborn}"
);
}