fix: fix some ui displays and features miss implemented
This commit is contained in:
@ -5,8 +5,8 @@ mod helpers;
|
||||
|
||||
use domain::{
|
||||
Agent, AgentManifest, AgentOrigin, AgentProfile, AgentTemplate, ContextInjection, DomainError,
|
||||
ManifestEntry, MarkdownDoc, ProfileId, Project, ProjectPath, PtySize, RemoteRef, SshAuth,
|
||||
TemplateId, TemplateVersion,
|
||||
ManifestEntry, MarkdownDoc, ProfileId, Project, ProjectPath, PtySize, RemoteRef, Skill, SkillId,
|
||||
SkillRef, SkillScope, SshAuth, TemplateId, TemplateVersion,
|
||||
};
|
||||
use helpers::{AtomicSeqIdGenerator, FixedClock};
|
||||
use uuid::Uuid;
|
||||
@ -409,3 +409,121 @@ fn manifest_unique_md_paths_ok() {
|
||||
.unwrap();
|
||||
assert!(AgentManifest::new(1, vec![e1, e2]).is_ok());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Skill invariants (L12, ARCHITECTURE §14.2)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
fn skill_id(n: u128) -> SkillId {
|
||||
SkillId::from_uuid(Uuid::from_u128(n))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skill_valid_construction() {
|
||||
let s = Skill::new(
|
||||
skill_id(1),
|
||||
"code-review",
|
||||
MarkdownDoc::new("review the diff"),
|
||||
SkillScope::Global,
|
||||
);
|
||||
assert!(s.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skill_rejects_empty_name() {
|
||||
let err = Skill::new(
|
||||
skill_id(1),
|
||||
"",
|
||||
MarkdownDoc::new("body"),
|
||||
SkillScope::Project,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(err, DomainError::EmptyField { field: "skill.name" });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skill_rejects_empty_content() {
|
||||
let err = Skill::new(skill_id(1), "x", MarkdownDoc::new(""), SkillScope::Global).unwrap_err();
|
||||
assert_eq!(
|
||||
err,
|
||||
DomainError::EmptyField {
|
||||
field: "skill.content_md"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skill_with_content_revalidates() {
|
||||
let s = Skill::new(skill_id(1), "x", MarkdownDoc::new("a"), SkillScope::Global).unwrap();
|
||||
assert!(s.with_content(MarkdownDoc::new("b")).is_ok());
|
||||
assert!(s.with_content(MarkdownDoc::new("")).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agent_assign_skill_is_idempotent() {
|
||||
let mut a = Agent::new(
|
||||
agent_id(1),
|
||||
"dev",
|
||||
"agents/dev.md",
|
||||
profile_id(),
|
||||
AgentOrigin::Scratch,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let r = SkillRef::new(skill_id(7), SkillScope::Global);
|
||||
assert!(a.assign_skill(r)); // first assignment
|
||||
assert!(!a.assign_skill(r)); // duplicate ignored
|
||||
assert_eq!(a.skills, vec![r]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agent_unassign_skill() {
|
||||
let mut a = Agent::new(
|
||||
agent_id(1),
|
||||
"dev",
|
||||
"agents/dev.md",
|
||||
profile_id(),
|
||||
AgentOrigin::Scratch,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let r = SkillRef::new(skill_id(7), SkillScope::Project);
|
||||
a.assign_skill(r);
|
||||
assert!(a.unassign_skill(skill_id(7)));
|
||||
assert!(!a.unassign_skill(skill_id(7))); // already gone
|
||||
assert!(a.skills.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agent_with_skills_dedups() {
|
||||
let r = SkillRef::new(skill_id(7), SkillScope::Global);
|
||||
let a = Agent::new(
|
||||
agent_id(1),
|
||||
"dev",
|
||||
"agents/dev.md",
|
||||
profile_id(),
|
||||
AgentOrigin::Scratch,
|
||||
false,
|
||||
)
|
||||
.unwrap()
|
||||
.with_skills(vec![r, r]);
|
||||
assert_eq!(a.skills, vec![r]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_entry_preserves_skills_through_agent_roundtrip() {
|
||||
let r = SkillRef::new(skill_id(7), SkillScope::Project);
|
||||
let agent = Agent::new(
|
||||
agent_id(1),
|
||||
"dev",
|
||||
"agents/dev.md",
|
||||
profile_id(),
|
||||
AgentOrigin::Scratch,
|
||||
false,
|
||||
)
|
||||
.unwrap()
|
||||
.with_skills(vec![r]);
|
||||
let entry = ManifestEntry::from_agent(&agent);
|
||||
assert_eq!(entry.skills, vec![r]);
|
||||
assert_eq!(entry.to_agent().unwrap(), agent);
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ mod helpers;
|
||||
use domain::{
|
||||
Agent, AgentManifest, AgentOrigin, AgentProfile, AgentTemplate, ContextInjection, Direction,
|
||||
LayoutNode, LayoutTree, LeafCell, ManifestEntry, MarkdownDoc, Project, ProjectPath, RemoteRef,
|
||||
SplitContainer, SshAuth, TemplateVersion, WeightedChild,
|
||||
Skill, SkillId, SkillRef, SkillScope, SplitContainer, SshAuth, TemplateVersion, WeightedChild,
|
||||
};
|
||||
use helpers::{node, session};
|
||||
use uuid::Uuid;
|
||||
@ -229,6 +229,61 @@ fn manifest_roundtrip_and_camel_case() {
|
||||
assert!(!json.contains("\"templateId\":null"), "json was {json}");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Skill (L12) — round-trip, camelCase scope tag, manifest skills back-compat
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
fn sid(n: u128) -> SkillId {
|
||||
SkillId::from_uuid(Uuid::from_u128(n))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skill_roundtrip_and_camel_case_scope() {
|
||||
let s = Skill::new(sid(1), "code-review", MarkdownDoc::new("body"), SkillScope::Global).unwrap();
|
||||
assert_eq!(roundtrip(&s), s);
|
||||
let json = serde_json::to_string(&s).unwrap();
|
||||
assert!(json.contains("\"scope\":\"global\""), "json was {json}");
|
||||
assert!(json.contains("\"contentMd\""), "json was {json}");
|
||||
|
||||
let p = Skill::new(sid(2), "simplify", MarkdownDoc::new("b"), SkillScope::Project).unwrap();
|
||||
let pj = serde_json::to_string(&p).unwrap();
|
||||
assert!(pj.contains("\"scope\":\"project\""), "json was {pj}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_entry_skills_roundtrip_and_camel_case() {
|
||||
let entry = ManifestEntry::from_agent(
|
||||
&Agent::new(
|
||||
aid(1),
|
||||
"dev",
|
||||
"agents/dev.md",
|
||||
profid(9),
|
||||
AgentOrigin::Scratch,
|
||||
false,
|
||||
)
|
||||
.unwrap()
|
||||
.with_skills(vec![SkillRef::new(sid(5), SkillScope::Project)]),
|
||||
);
|
||||
assert_eq!(roundtrip(&entry), entry);
|
||||
let json = serde_json::to_string(&entry).unwrap();
|
||||
assert!(json.contains("\"skillId\""), "json was {json}");
|
||||
assert!(json.contains("\"scope\":\"project\""), "json was {json}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_entry_without_skills_omits_field_and_deserialises() {
|
||||
// An entry with no skills must not emit "skills" (skip_serializing_if),
|
||||
// and a pre-L12 manifest JSON (no skills key) must deserialise to empty.
|
||||
let entry =
|
||||
ManifestEntry::new(aid(1), "dev", "agents/dev.md", profid(9), None, false, None).unwrap();
|
||||
let json = serde_json::to_string(&entry).unwrap();
|
||||
assert!(!json.contains("\"skills\""), "json was {json}");
|
||||
|
||||
let legacy = r#"{"agentId":"00000000-0000-0000-0000-000000000001","name":"dev","mdPath":"agents/dev.md","profileId":"00000000-0000-0000-0000-000000000009","synchronized":false}"#;
|
||||
let parsed: ManifestEntry = serde_json::from_str(legacy).unwrap();
|
||||
assert!(parsed.skills.is_empty());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// LayoutTree (tagged enum: type/node)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user