mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
test(goal): checklist codec + service coverage; final internal-ref sweep
This commit is contained in:
parent
da332f2fd8
commit
77196acbcd
@ -42,7 +42,7 @@ public enum GoalStatus {
|
||||
* the DB stores via {@link EnumValue}. Without this, Jackson defaults to
|
||||
* {@link #name()} (uppercase) and the frontend's
|
||||
* {@code status: 'active' | 'paused' | ...} TS literal types reject
|
||||
* every payload — UI bug observed during PR4 manual QA.
|
||||
* every payload.
|
||||
*/
|
||||
@JsonValue
|
||||
public String getValue() {
|
||||
|
||||
@ -0,0 +1,113 @@
|
||||
package vip.mate.goal.model;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* Pure unit tests for the checklist (de)serialization + merge helpers.
|
||||
*/
|
||||
class GoalCriteriaCodecTest {
|
||||
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
private static GoalCriterion c(String id, String text, boolean passed) {
|
||||
return new GoalCriterion(id, text, passed, passed ? "ok" : "");
|
||||
}
|
||||
|
||||
// ---------- parse ----------
|
||||
|
||||
@Test
|
||||
void parse_nullOrBlankOrCorrupt_returnsEmptyMutableList() {
|
||||
assertTrue(GoalCriteriaCodec.parse(null, mapper).isEmpty());
|
||||
assertTrue(GoalCriteriaCodec.parse("", mapper).isEmpty());
|
||||
assertTrue(GoalCriteriaCodec.parse(" ", mapper).isEmpty());
|
||||
assertTrue(GoalCriteriaCodec.parse("{not valid json", mapper).isEmpty());
|
||||
// mutable: callers append during bootstrap/append paths
|
||||
GoalCriteriaCodec.parse(null, mapper).add(c("C1", "x", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void parse_roundTrip() {
|
||||
String json = GoalCriteriaCodec.serialize(List.of(c("C1", "tests pass", true)), mapper);
|
||||
List<GoalCriterion> back = GoalCriteriaCodec.parse(json, mapper);
|
||||
assertEquals(1, back.size());
|
||||
assertEquals("C1", back.get(0).id());
|
||||
assertEquals("tests pass", back.get(0).text());
|
||||
assertTrue(back.get(0).passed());
|
||||
}
|
||||
|
||||
@Test
|
||||
void serialize_null_returnsNull() {
|
||||
assertNull(GoalCriteriaCodec.serialize(null, mapper));
|
||||
}
|
||||
|
||||
// ---------- merge ----------
|
||||
|
||||
@Test
|
||||
void merge_appliesVerdictById_preservesTextAndUntouched() {
|
||||
List<GoalCriterion> existing = List.of(
|
||||
c("C1", "first", false),
|
||||
c("C2", "second", false));
|
||||
List<GoalChecklistVerdict.CriterionVerdict> delta = List.of(
|
||||
new GoalChecklistVerdict.CriterionVerdict("C1", true, "did it"));
|
||||
|
||||
List<GoalCriterion> merged = GoalCriteriaCodec.merge(existing, delta);
|
||||
|
||||
assertEquals(2, merged.size());
|
||||
assertTrue(merged.get(0).passed());
|
||||
assertEquals("did it", merged.get(0).evidence());
|
||||
assertEquals("first", merged.get(0).text()); // text preserved
|
||||
assertFalse(merged.get(1).passed()); // untouched stays
|
||||
assertEquals("second", merged.get(1).text());
|
||||
}
|
||||
|
||||
@Test
|
||||
void merge_unknownVerdictId_isIgnored() {
|
||||
List<GoalCriterion> existing = List.of(c("C1", "first", false));
|
||||
List<GoalChecklistVerdict.CriterionVerdict> delta = List.of(
|
||||
new GoalChecklistVerdict.CriterionVerdict("C9", true, "nope"));
|
||||
List<GoalCriterion> merged = GoalCriteriaCodec.merge(existing, delta);
|
||||
assertFalse(merged.get(0).passed());
|
||||
}
|
||||
|
||||
// ---------- allPassed / remaining ----------
|
||||
|
||||
@Test
|
||||
void allPassed_emptyIsFalse() {
|
||||
assertFalse(GoalCriteriaCodec.allPassed(List.of()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void allPassed_trueOnlyWhenEveryPassed() {
|
||||
assertTrue(GoalCriteriaCodec.allPassed(List.of(c("C1", "a", true), c("C2", "b", true))));
|
||||
assertFalse(GoalCriteriaCodec.allPassed(List.of(c("C1", "a", true), c("C2", "b", false))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void remaining_returnsOnlyUnpassed() {
|
||||
List<GoalCriterion> rem = GoalCriteriaCodec.remaining(
|
||||
List.of(c("C1", "a", true), c("C2", "b", false), c("C3", "c", false)));
|
||||
assertEquals(2, rem.size());
|
||||
assertEquals("C2", rem.get(0).id());
|
||||
assertEquals("C3", rem.get(1).id());
|
||||
}
|
||||
|
||||
// ---------- reindex ----------
|
||||
|
||||
@Test
|
||||
void reindex_assignsSequentialIds() {
|
||||
List<GoalCriterion> out = GoalCriteriaCodec.reindex(List.of(
|
||||
c("", "a", false), c("zzz", "b", false), c("C99", "c", false)));
|
||||
assertEquals("C1", out.get(0).id());
|
||||
assertEquals("C2", out.get(1).id());
|
||||
assertEquals("C3", out.get(2).id());
|
||||
assertEquals("a", out.get(0).text());
|
||||
}
|
||||
}
|
||||
@ -314,6 +314,70 @@ class GoalServiceTest {
|
||||
verify(goalMapper, never()).selectById(any());
|
||||
}
|
||||
|
||||
// ==================== criteria checklist ====================
|
||||
|
||||
@Test
|
||||
void create_normalizesInitialCriteria_assignsIdsForcesUnpassed() {
|
||||
when(goalMapper.selectOne(any())).thenReturn(null);
|
||||
when(goalMapper.insert(any(GoalEntity.class))).thenReturn(1);
|
||||
|
||||
GoalCreateRequest r = validReq();
|
||||
r.setCriteria(java.util.List.of(
|
||||
new vip.mate.goal.model.GoalCriterion("ignored", "tests pass", true, "x"),
|
||||
new vip.mate.goal.model.GoalCriterion("", " ", false, ""), // blank dropped
|
||||
new vip.mate.goal.model.GoalCriterion("", "deployed", false, "")));
|
||||
|
||||
ArgumentCaptor<GoalEntity> captor = ArgumentCaptor.forClass(GoalEntity.class);
|
||||
service.create(r, "alice");
|
||||
verify(goalMapper).insert(captor.capture());
|
||||
|
||||
java.util.List<vip.mate.goal.model.GoalCriterion> parsed =
|
||||
vip.mate.goal.model.GoalCriteriaCodec.parse(captor.getValue().getCriteria(), new ObjectMapper());
|
||||
assertEquals(2, parsed.size());
|
||||
assertEquals("C1", parsed.get(0).id());
|
||||
assertEquals("tests pass", parsed.get(0).text());
|
||||
assertFalse(parsed.get(0).passed()); // forced false even though caller said true
|
||||
assertEquals("C2", parsed.get(1).id());
|
||||
assertEquals("deployed", parsed.get(1).text());
|
||||
}
|
||||
|
||||
@Test
|
||||
void create_emptyCriteria_leavesColumnNull_forBootstrap() {
|
||||
when(goalMapper.selectOne(any())).thenReturn(null);
|
||||
when(goalMapper.insert(any(GoalEntity.class))).thenReturn(1);
|
||||
ArgumentCaptor<GoalEntity> captor = ArgumentCaptor.forClass(GoalEntity.class);
|
||||
service.create(validReq(), "alice");
|
||||
verify(goalMapper).insert(captor.capture());
|
||||
assertNull(captor.getValue().getCriteria());
|
||||
}
|
||||
|
||||
@Test
|
||||
void create_autoFollowup_threeState() {
|
||||
when(goalMapper.selectOne(any())).thenReturn(null);
|
||||
when(goalMapper.insert(any(GoalEntity.class))).thenReturn(1);
|
||||
|
||||
// null -> config default (true by default)
|
||||
assertTrue(service.create(validReq(), "alice").getAutoFollowupEnabled());
|
||||
|
||||
// explicit false is honored
|
||||
GoalCreateRequest off = validReq();
|
||||
off.setAutoFollowupEnabled(false);
|
||||
assertFalse(service.create(off, "alice").getAutoFollowupEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
void toResponse_parsesCriteriaArray_nullBecomesEmpty() {
|
||||
GoalEntity g = persisted(1L, GoalStatus.ACTIVE);
|
||||
g.setCriteria("[{\"id\":\"C1\",\"text\":\"a\",\"passed\":true,\"evidence\":\"ok\"}]");
|
||||
var resp = service.toResponse(g);
|
||||
assertEquals(1, resp.getCriteria().size());
|
||||
assertTrue(resp.getCriteria().get(0).passed());
|
||||
|
||||
GoalEntity bare = persisted(2L, GoalStatus.ACTIVE); // criteria == null
|
||||
assertNotNull(service.toResponse(bare).getCriteria());
|
||||
assertTrue(service.toResponse(bare).getCriteria().isEmpty());
|
||||
}
|
||||
|
||||
// ==================== optimistic lock retry ====================
|
||||
|
||||
@Test
|
||||
|
||||
Loading…
Reference in New Issue
Block a user