elixir: add model and workspace test suites (22 tests)

Swadhin Biswas committed 7 months ago
fcd1d5c
1 parent a407de2
commit fcd1d5c
3 files changed
+273 -0
▼ELIXIR/test/models_test.exs
+88-0
1
+defmodule Contexa.ModelsTest do
2
+ use ExUnit.Case, async: true
3
+
4
+ alias Contexa.Models
5
+ alias Contexa.Models.{OTARecord, CommitRecord, BranchMetadata}
6
+
7
+ test "generate_id returns 8 hex chars" do
8
+ id = Models.generate_id()
9
+ assert String.length(id) == 8
10
+ assert Regex.match?(~r/^[0-9a-f]+$/, id)
11
+ end
12
+
13
+ test "timestamp returns ISO 8601" do
14
+ ts = Models.timestamp()
15
+ assert Regex.match?(~r/^\d{4}-\d{2}-\d{2}T/, ts)
16
+ end
17
+
18
+ test "OTA to_markdown renders correctly" do
19
+ rec = %OTARecord{step: 1, timestamp: "2026-01-01T00:00:00Z",
20
+ observation: "saw something", thought: "thinking", action: "did something"}
21
+ md = Models.ota_to_markdown(rec)
22
+ assert md =~ "### Step 1"
23
+ assert md =~ "saw something"
24
+ assert md =~ "thinking"
25
+ assert md =~ "did something"
26
+ end
27
+
28
+ test "Commit to_markdown renders correctly" do
29
+ rec = %CommitRecord{commit_id: "abc12345", branch_name: "main",
30
+ branch_purpose: "purpose", previous_progress_summary: "prev",
31
+ this_commit_contribution: "contrib", timestamp: "2026-01-01T00:00:00Z"}
32
+ md = Models.commit_to_markdown(rec)
33
+ assert md =~ "## Commit `abc12345`"
34
+ assert md =~ "purpose"
35
+ assert md =~ "contrib"
36
+ end
37
+
38
+ test "BranchMetadata YAML round-trip" do
39
+ meta = Models.new_branch_metadata("test-branch", "test purpose", "main")
40
+ yaml = Models.metadata_to_yaml(meta)
41
+ parsed = Models.metadata_from_yaml(yaml)
42
+ assert parsed.name == "test-branch"
43
+ assert parsed.purpose == "test purpose"
44
+ assert parsed.created_from == "main"
45
+ assert parsed.status == "active"
46
+ assert parsed.merged_into == nil
47
+ end
48
+
49
+ test "parse_commits extracts records" do
50
+ text = """
51
+ # Header
52
+
53
+ ## Commit `abc12345`
54
+ **Timestamp:** 2026-01-01T00:00:00Z
55
+
56
+ **Branch Purpose:** test
57
+
58
+ **Previous Progress Summary:** prev
59
+
60
+ **This Commit's Contribution:** contrib
61
+
62
+ ---
63
+ """
64
+ commits = Models.parse_commits(text)
65
+ assert length(commits) == 1
66
+ assert hd(commits).commit_id == "abc12345"
67
+ assert hd(commits).this_commit_contribution == "contrib"
68
+ end
69
+
70
+ test "parse_ota extracts records" do
71
+ text = """
72
+ # Header
73
+
74
+ ### Step 1 — 2026-01-01T00:00:00Z
75
+ **Observation:** obs1
76
+
77
+ **Thought:** thought1
78
+
79
+ **Action:** action1
80
+
81
+ ---
82
+ """
83
+ records = Models.parse_ota(text)
84
+ assert length(records) == 1
85
+ assert hd(records).step == 1
86
+ assert hd(records).observation == "obs1"
87
+ end
88
+end
0
89
▼ELIXIR/test/test_helper.exs
+1-0
1
+ExUnit.start()
0
2
▼ELIXIR/test/workspace_test.exs
+184-0
1
+defmodule Contexa.WorkspaceTest do
2
+ use ExUnit.Case, async: true
3
+
4
+ alias Contexa.Workspace
5
+ alias Contexa.Models
6
+
7
+ defp make_tmp do
8
+ dir = Path.join(System.tmp_dir!(), "contexa_ex_test_#{System.unique_integer([:positive])}")
9
+ File.rm_rf!(dir)
10
+ File.mkdir_p!(dir)
11
+ dir
12
+ end
13
+
14
+ defp cleanup(dir) do
15
+ File.rm_rf!(dir)
16
+ end
17
+
18
+ test "init creates workspace" do
19
+ dir = make_tmp()
20
+ ws = Workspace.new(dir) |> Workspace.init("Test roadmap")
21
+ assert ws.current_branch == "main"
22
+ assert File.dir?(Path.join(dir, ".GCC"))
23
+ cleanup(dir)
24
+ end
25
+
26
+ test "init fails if already exists" do
27
+ dir = make_tmp()
28
+ ws = Workspace.new(dir) |> Workspace.init()
29
+ assert_raise RuntimeError, fn ->
30
+ Workspace.init(ws)
31
+ end
32
+ cleanup(dir)
33
+ end
34
+
35
+ test "load attaches to existing workspace" do
36
+ dir = make_tmp()
37
+ Workspace.new(dir) |> Workspace.init()
38
+ ws = Workspace.new(dir) |> Workspace.load()
39
+ assert ws.current_branch == "main"
40
+ cleanup(dir)
41
+ end
42
+
43
+ test "load fails if no workspace" do
44
+ dir = make_tmp()
45
+ assert_raise RuntimeError, fn ->
46
+ Workspace.new(dir) |> Workspace.load()
47
+ end
48
+ cleanup(dir)
49
+ end
50
+
51
+ test "log_ota appends records with incrementing steps" do
52
+ dir = make_tmp()
53
+ ws = Workspace.new(dir) |> Workspace.init()
54
+ {ws, r1} = Workspace.log_ota(ws, "obs1", "thought1", "act1")
55
+ assert r1.step == 1
56
+ {_ws, r2} = Workspace.log_ota(ws, "obs2", "thought2", "act2")
57
+ assert r2.step == 2
58
+ cleanup(dir)
59
+ end
60
+
61
+ test "commit creates record with auto previous_summary" do
62
+ dir = make_tmp()
63
+ ws = Workspace.new(dir) |> Workspace.init()
64
+ {ws, c1} = Workspace.commit(ws, "First contribution")
65
+ assert String.length(c1.commit_id) == 8
66
+ {_ws, c2} = Workspace.commit(ws, "Second contribution")
67
+ assert c2.previous_progress_summary == "First contribution"
68
+ cleanup(dir)
69
+ end
70
+
71
+ test "branch creates isolated workspace" do
72
+ dir = make_tmp()
73
+ ws = Workspace.new(dir) |> Workspace.init()
74
+ {ws, _} = Workspace.log_ota(ws, "obs", "thought", "action")
75
+ ws = Workspace.branch(ws, "feature", "Test feature")
76
+ assert ws.current_branch == "feature"
77
+ ctx = Workspace.context(ws, "feature", 10)
78
+ assert length(ctx.ota_records) == 0
79
+ assert length(ctx.commits) == 0
80
+ cleanup(dir)
81
+ end
82
+
83
+ test "branch fails if duplicate name" do
84
+ dir = make_tmp()
85
+ ws = Workspace.new(dir) |> Workspace.init()
86
+ ws = Workspace.branch(ws, "dup", "purpose")
87
+ ws = Workspace.switch_branch(ws, "main")
88
+ assert_raise RuntimeError, fn ->
89
+ Workspace.branch(ws, "dup", "purpose")
90
+ end
91
+ cleanup(dir)
92
+ end
93
+
94
+ test "switch_branch changes current branch" do
95
+ dir = make_tmp()
96
+ ws = Workspace.new(dir) |> Workspace.init()
97
+ ws = Workspace.branch(ws, "feature", "purpose")
98
+ ws = Workspace.switch_branch(ws, "main")
99
+ assert ws.current_branch == "main"
100
+ cleanup(dir)
101
+ end
102
+
103
+ test "switch_branch fails on non-existent branch" do
104
+ dir = make_tmp()
105
+ ws = Workspace.new(dir) |> Workspace.init()
106
+ assert_raise RuntimeError, fn ->
107
+ Workspace.switch_branch(ws, "nope")
108
+ end
109
+ cleanup(dir)
110
+ end
111
+
112
+ test "list_branches returns all branches" do
113
+ dir = make_tmp()
114
+ ws = Workspace.new(dir) |> Workspace.init()
115
+ ws = Workspace.branch(ws, "alpha", "purpose a")
116
+ ws = Workspace.switch_branch(ws, "main")
117
+ ws = Workspace.branch(ws, "beta", "purpose b")
118
+ branches = Workspace.list_branches(ws)
119
+ assert length(branches) >= 3
120
+ cleanup(dir)
121
+ end
122
+
123
+ test "merge integrates branch into target" do
124
+ dir = make_tmp()
125
+ ws = Workspace.new(dir) |> Workspace.init()
126
+ ws = Workspace.branch(ws, "feature", "Add feature X")
127
+ {ws, _} = Workspace.log_ota(ws, "obs", "thought", "action")
128
+ {ws, _} = Workspace.commit(ws, "Implemented feature X")
129
+ {ws, merge_rec} = Workspace.merge(ws, "feature", nil, "main")
130
+ assert ws.current_branch == "main"
131
+ assert String.length(merge_rec.commit_id) == 8
132
+ ctx = Workspace.context(ws, "main", 10)
133
+ assert length(ctx.commits) >= 1
134
+ cleanup(dir)
135
+ end
136
+
137
+ test "merge fails on non-existent source" do
138
+ dir = make_tmp()
139
+ ws = Workspace.new(dir) |> Workspace.init()
140
+ assert_raise RuntimeError, fn ->
141
+ Workspace.merge(ws, "nope")
142
+ end
143
+ cleanup(dir)
144
+ end
145
+
146
+ test "context returns correct data" do
147
+ dir = make_tmp()
148
+ ws = Workspace.new(dir) |> Workspace.init("My roadmap")
149
+ {ws, _} = Workspace.log_ota(ws, "obs1", "t1", "a1")
150
+ {ws, _} = Workspace.log_ota(ws, "obs2", "t2", "a2")
151
+ {ws, _} = Workspace.commit(ws, "Commit 1")
152
+ {ws, _} = Workspace.commit(ws, "Commit 2")
153
+ {ws, _} = Workspace.commit(ws, "Commit 3")
154
+ ctx = Workspace.context(ws, "main", 2)
155
+ assert length(ctx.commits) == 2
156
+ assert length(ctx.ota_records) == 2
157
+ assert ctx.main_roadmap =~ "My roadmap"
158
+ assert ctx.metadata != nil
159
+ assert ctx.metadata.name == "main"
160
+ cleanup(dir)
161
+ end
162
+
163
+ test "context k larger than available commits" do
164
+ dir = make_tmp()
165
+ ws = Workspace.new(dir) |> Workspace.init()
166
+ {ws, _} = Workspace.commit(ws, "Only commit")
167
+ ctx = Workspace.context(ws, "main", 100)
168
+ assert length(ctx.commits) == 1
169
+ cleanup(dir)
170
+ end
171
+
172
+ test "context_summary renders markdown" do
173
+ dir = make_tmp()
174
+ ws = Workspace.new(dir) |> Workspace.init("Roadmap text")
175
+ {ws, _} = Workspace.log_ota(ws, "obs", "thought", "action")
176
+ {ws, _} = Workspace.commit(ws, "My contribution")
177
+ ctx = Workspace.context(ws, "main", 1)
178
+ summary = Models.context_summary(ctx)
179
+ assert summary =~ "CONTEXT"
180
+ assert summary =~ "Roadmap"
181
+ assert summary =~ "My contribution"
182
+ cleanup(dir)
183
+ end
184
+end
0
185