feat: add main features

Agents for developpement added + frontend add + backend added. Git viewer created + agent and template creator + layout and project creator
This commit is contained in:
2026-06-06 01:27:01 +02:00
parent 55b3bee2c8
commit 307ae71857
273 changed files with 48740 additions and 0 deletions

View File

@ -0,0 +1,158 @@
//! L9 tests for [`ConnectRemote`] with a mock [`RemoteHost`]. The same use case
//! must behave identically whatever the host kind (Liskov), so we drive it with a
//! fake host parameterised by kind + root reachability.
use std::sync::{Arc, Mutex};
use async_trait::async_trait;
use domain::events::DomainEvent;
use domain::ports::{
DirEntry, EventBus, EventStream, FileSystem, FsError, ProcessSpawner, PtyPort, RemoteError,
RemoteHost, RemotePath,
};
use domain::{ProjectId, RemoteKind};
use uuid::Uuid;
use application::{ConnectRemote, ConnectRemoteInput};
// --- Fake filesystem (only `exists` matters here) -------------------------
struct FakeFs {
existing_root: Option<String>,
}
#[async_trait]
impl FileSystem for FakeFs {
async fn read(&self, p: &RemotePath) -> Result<Vec<u8>, FsError> {
Err(FsError::NotFound(p.as_str().to_owned()))
}
async fn write(&self, _p: &RemotePath, _d: &[u8]) -> Result<(), FsError> {
Ok(())
}
async fn exists(&self, p: &RemotePath) -> Result<bool, FsError> {
Ok(self.existing_root.as_deref() == Some(p.as_str()))
}
async fn create_dir_all(&self, _p: &RemotePath) -> Result<(), FsError> {
Ok(())
}
async fn list(&self, _p: &RemotePath) -> Result<Vec<DirEntry>, FsError> {
Ok(Vec::new())
}
async fn symlink(&self, _s: &RemotePath, _d: &RemotePath) -> Result<(), FsError> {
Ok(())
}
}
// --- Fake remote host -----------------------------------------------------
struct FakeHost {
kind: RemoteKind,
connect_ok: bool,
fs: Arc<dyn FileSystem>,
}
impl FakeHost {
fn make(kind: RemoteKind, connect_ok: bool, existing_root: Option<&str>) -> Arc<dyn RemoteHost> {
Arc::new(Self {
kind,
connect_ok,
fs: Arc::new(FakeFs {
existing_root: existing_root.map(ToOwned::to_owned),
}),
})
}
}
#[async_trait]
impl RemoteHost for FakeHost {
fn kind(&self) -> RemoteKind {
self.kind
}
async fn connect(&self) -> Result<(), RemoteError> {
if self.connect_ok {
Ok(())
} else {
Err(RemoteError::Connection("refused".to_owned()))
}
}
fn file_system(&self) -> Arc<dyn FileSystem> {
Arc::clone(&self.fs)
}
fn process_spawner(&self) -> Arc<dyn ProcessSpawner> {
unreachable!("ConnectRemote does not use the spawner")
}
fn pty(&self) -> Arc<dyn PtyPort> {
unreachable!("ConnectRemote does not use the pty")
}
}
#[derive(Default, Clone)]
struct SpyBus(Arc<Mutex<Vec<DomainEvent>>>);
impl SpyBus {
fn events(&self) -> Vec<DomainEvent> {
self.0.lock().unwrap().clone()
}
}
impl EventBus for SpyBus {
fn publish(&self, e: DomainEvent) {
self.0.lock().unwrap().push(e);
}
fn subscribe(&self) -> EventStream {
Box::new(std::iter::empty())
}
}
fn pid() -> ProjectId {
ProjectId::from_uuid(Uuid::from_u128(1))
}
#[tokio::test]
async fn connect_succeeds_and_emits_event_for_any_host_kind() {
// Liskov: identical behaviour for Local, Ssh and Wsl hosts.
for kind in [RemoteKind::Local, RemoteKind::Ssh, RemoteKind::Wsl] {
let host = FakeHost::make(kind, true, Some("/srv/app"));
let bus = SpyBus::default();
let out = ConnectRemote::new(Arc::new(bus.clone()))
.execute(ConnectRemoteInput {
host,
project_id: pid(),
root: "/srv/app".to_owned(),
})
.await
.unwrap();
assert_eq!(out.kind, kind);
assert_eq!(
bus.events(),
vec![DomainEvent::RemoteConnected { project_id: pid() }]
);
}
}
#[tokio::test]
async fn connect_propagates_connection_failure() {
let host = FakeHost::make(RemoteKind::Ssh, false, Some("/srv/app"));
let bus = SpyBus::default();
let err = ConnectRemote::new(Arc::new(bus.clone()))
.execute(ConnectRemoteInput {
host,
project_id: pid(),
root: "/srv/app".to_owned(),
})
.await
.unwrap_err();
assert_eq!(err.code(), "REMOTE", "got {err:?}");
assert!(bus.events().is_empty());
}
#[tokio::test]
async fn connect_fails_when_root_unreachable() {
let host = FakeHost::make(RemoteKind::Local, true, Some("/other"));
let bus = SpyBus::default();
let err = ConnectRemote::new(Arc::new(bus.clone()))
.execute(ConnectRemoteInput {
host,
project_id: pid(),
root: "/srv/app".to_owned(),
})
.await
.unwrap_err();
assert_eq!(err.code(), "NOT_FOUND", "got {err:?}");
assert!(bus.events().is_empty(), "no event when root is missing");
}