Skip to content

Commit f6935f4

Browse files
committed
Stabilize plugin authoring temp directory tests
1 parent 1f15571 commit f6935f4

7 files changed

Lines changed: 90 additions & 38 deletions

File tree

test/codex/plugins/marketplace_test.exs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ defmodule Codex.Plugins.MarketplaceTest do
33

44
alias Codex.Plugins
55
alias Codex.Plugins.Marketplace
6+
alias Codex.TestSupport.TempDir
67

78
test "minimal valid marketplace parses nested policy fields and preserves unknown fields" do
89
payload = %{
@@ -98,7 +99,10 @@ defmodule Codex.Plugins.MarketplaceTest do
9899
end
99100

100101
test "marketplace source paths must stay inside the marketplace root" do
101-
temp_root = temp_root("marketplace_containment")
102+
temp_root =
103+
TempDir.create!("marketplace_containment")
104+
|> tap(&on_exit(fn -> File.rm_rf!(&1) end))
105+
102106
marketplace_path = Path.join([temp_root, "repo", ".agents", "plugins", "marketplace.json"])
103107

104108
File.mkdir_p!(Path.dirname(marketplace_path))
@@ -133,7 +137,10 @@ defmodule Codex.Plugins.MarketplaceTest do
133137
end
134138

135139
test "marketplace source paths reject traversal segments even when they remain under the root" do
136-
temp_root = temp_root("marketplace_traversal")
140+
temp_root =
141+
TempDir.create!("marketplace_traversal")
142+
|> tap(&on_exit(fn -> File.rm_rf!(&1) end))
143+
137144
marketplace_path = Path.join([temp_root, "repo", ".agents", "plugins", "marketplace.json"])
138145

139146
File.mkdir_p!(Path.dirname(marketplace_path))
@@ -166,8 +173,4 @@ defmodule Codex.Plugins.MarketplaceTest do
166173
%{source_path: "./plugins/demo-plugin/../other-plugin", path: ^marketplace_path}}} =
167174
Plugins.read_marketplace(marketplace_path)
168175
end
169-
170-
defp temp_root(prefix) do
171-
Path.join(System.tmp_dir!(), "#{prefix}_#{System.unique_integer([:positive])}")
172-
end
173176
end

test/codex/plugins/paths_test.exs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ defmodule Codex.Plugins.PathsTest do
22
use ExUnit.Case, async: true
33

44
alias Codex.Plugins.Paths
5+
alias Codex.TestSupport.TempDir
56

67
test "repo and personal scopes resolve the correct roots and canonical file paths" do
7-
temp_root = temp_root("plugin_paths")
8+
temp_root =
9+
TempDir.create!("plugin_paths")
10+
|> tap(&on_exit(fn -> File.rm_rf!(&1) end))
11+
812
repo_root = Path.join(temp_root, "repo")
913
nested = Path.join(repo_root, "apps/demo")
1014
home_root = Path.join(temp_root, "home")
@@ -33,8 +37,4 @@ defmodule Codex.Plugins.PathsTest do
3337
assert {:ok, ^expected_personal_marketplace_path} =
3438
Paths.marketplace_path(:personal, home: home_root)
3539
end
36-
37-
defp temp_root(prefix) do
38-
Path.join(System.tmp_dir!(), "#{prefix}_#{System.unique_integer([:positive])}")
39-
end
4040
end

test/codex/plugins/scaffold_test.exs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ defmodule Codex.Plugins.ScaffoldTest do
22
use ExUnit.Case, async: true
33

44
alias Codex.Plugins
5+
alias Codex.TestSupport.TempDir
56

67
test "repo-scope scaffold creates the minimal plugin tree and optional marketplace entry" do
7-
temp_root = temp_root("plugin_scaffold_repo")
8+
temp_root =
9+
TempDir.create!("plugin_scaffold_repo")
10+
|> tap(&on_exit(fn -> File.rm_rf!(&1) end))
11+
812
repo_root = Path.join(temp_root, "repo")
913

1014
File.mkdir_p!(Path.join(repo_root, ".git"))
@@ -41,7 +45,10 @@ defmodule Codex.Plugins.ScaffoldTest do
4145
end
4246

4347
test "personal-scope scaffold resolves to the home-local plugin and marketplace roots" do
44-
temp_root = temp_root("plugin_scaffold_personal")
48+
temp_root =
49+
TempDir.create!("plugin_scaffold_personal")
50+
|> tap(&on_exit(fn -> File.rm_rf!(&1) end))
51+
4552
home_root = Path.join(temp_root, "home")
4653
File.mkdir_p!(home_root)
4754

@@ -58,12 +65,4 @@ defmodule Codex.Plugins.ScaffoldTest do
5865
assert File.regular?(result.manifest_path)
5966
assert File.regular?(result.marketplace_path)
6067
end
61-
62-
defp temp_root(prefix) do
63-
Path.join(System.tmp_dir!(), "#{prefix}_#{unique_suffix()}")
64-
end
65-
66-
defp unique_suffix do
67-
Base.encode16(:crypto.strong_rand_bytes(8), case: :lower)
68-
end
6968
end

test/codex/plugins/writer_test.exs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ defmodule Codex.Plugins.WriterTest do
22
use ExUnit.Case, async: true
33

44
alias Codex.Plugins
5+
alias Codex.TestSupport.TempDir
56

67
test "manifest writes are deterministic, pretty, and newline terminated" do
7-
temp_root = temp_root("plugin_writer_manifest")
8+
temp_root =
9+
TempDir.create!("plugin_writer_manifest")
10+
|> tap(&on_exit(fn -> File.rm_rf!(&1) end))
11+
812
manifest_path = Path.join([temp_root, "demo-plugin", ".codex-plugin", "plugin.json"])
913

1014
{:ok, manifest} =
@@ -27,7 +31,10 @@ defmodule Codex.Plugins.WriterTest do
2731
end
2832

2933
test "overwrite protection prevents silent clobbering" do
30-
temp_root = temp_root("plugin_writer_overwrite")
34+
temp_root =
35+
TempDir.create!("plugin_writer_overwrite")
36+
|> tap(&on_exit(fn -> File.rm_rf!(&1) end))
37+
3138
manifest_path = Path.join([temp_root, "demo-plugin", ".codex-plugin", "plugin.json"])
3239

3340
File.mkdir_p!(Path.dirname(manifest_path))
@@ -40,7 +47,10 @@ defmodule Codex.Plugins.WriterTest do
4047
end
4148

4249
test "marketplace updates merge new plugins without erasing unrelated entries" do
43-
temp_root = temp_root("plugin_writer_marketplace")
50+
temp_root =
51+
TempDir.create!("plugin_writer_marketplace")
52+
|> tap(&on_exit(fn -> File.rm_rf!(&1) end))
53+
4454
repo_root = Path.join(temp_root, "repo")
4555
marketplace_path = Path.join(repo_root, ".agents/plugins/marketplace.json")
4656

@@ -89,7 +99,10 @@ defmodule Codex.Plugins.WriterTest do
8999
end
90100

91101
test "overwrite updates preserve unknown fields on the replaced marketplace entry" do
92-
temp_root = temp_root("plugin_writer_overwrite_preserve")
102+
temp_root =
103+
TempDir.create!("plugin_writer_overwrite_preserve")
104+
|> tap(&on_exit(fn -> File.rm_rf!(&1) end))
105+
93106
repo_root = Path.join(temp_root, "repo")
94107
marketplace_path = Path.join(repo_root, ".agents/plugins/marketplace.json")
95108

@@ -157,12 +170,4 @@ defmodule Codex.Plugins.WriterTest do
157170
}
158171
] = marketplace.plugins
159172
end
160-
161-
defp temp_root(prefix) do
162-
Path.join(System.tmp_dir!(), "#{prefix}_#{unique_suffix()}")
163-
end
164-
165-
defp unique_suffix do
166-
Base.encode16(:crypto.strong_rand_bytes(8), case: :lower)
167-
end
168173
end

test/integration/plugin_authoring_verification_test.exs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ defmodule Codex.Integration.PluginAuthoringVerificationTest do
66
alias Codex.Options
77
alias Codex.Plugins
88
alias Codex.Protocol.Plugin
9+
alias Codex.TestSupport.TempDir
910

1011
@moduletag :integration
1112

@@ -15,7 +16,10 @@ defmodule Codex.Integration.PluginAuthoringVerificationTest do
1516
:ok
1617

1718
{:ok, codex_opts} ->
18-
temp_root = temp_root("plugin_authoring_verification")
19+
temp_root =
20+
TempDir.create!("plugin_authoring_verification")
21+
|> tap(&on_exit(fn -> File.rm_rf!(&1) end))
22+
1923
repo_root = Path.join(temp_root, "repo")
2024
home_root = Path.join(temp_root, "home")
2125
codex_home = Path.join(home_root, ".codex")
@@ -97,10 +101,6 @@ defmodule Codex.Integration.PluginAuthoringVerificationTest do
97101
end
98102
end
99103

100-
defp temp_root(prefix) do
101-
Path.join(System.tmp_dir!(), "#{prefix}_#{System.unique_integer([:positive])}")
102-
end
103-
104104
defp run_command_spec(%CommandSpec{} = spec, args) when is_list(args) do
105105
System.cmd(spec.program, CommandSpec.command_args(spec, args), stderr_to_stdout: true)
106106
end

test/support/temp_dir.ex

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
defmodule Codex.TestSupport.TempDir do
2+
@moduledoc false
3+
4+
@max_attempts 32
5+
6+
def create!(prefix) when is_binary(prefix) do
7+
prefix
8+
|> sanitize_prefix()
9+
|> create_unique!(@max_attempts)
10+
end
11+
12+
defp create_unique!(_prefix, 0) do
13+
raise "unable to allocate a unique temporary directory"
14+
end
15+
16+
defp create_unique!(prefix, attempts_left) when attempts_left > 0 do
17+
path = Path.join(System.tmp_dir!(), "#{prefix}_#{unique_suffix()}")
18+
19+
case File.mkdir(path) do
20+
:ok ->
21+
path
22+
23+
{:error, :eexist} ->
24+
create_unique!(prefix, attempts_left - 1)
25+
26+
{:error, reason} ->
27+
raise "unable to create temporary directory #{inspect(path)}: #{inspect(reason)}"
28+
end
29+
end
30+
31+
defp sanitize_prefix(prefix) do
32+
prefix
33+
|> String.trim()
34+
|> String.replace(~r/[^a-zA-Z0-9_-]+/, "_")
35+
|> case do
36+
"" -> "codex_tmp"
37+
sanitized -> sanitized
38+
end
39+
end
40+
41+
defp unique_suffix do
42+
Base.encode16(:crypto.strong_rand_bytes(10), case: :lower)
43+
end
44+
end

test/test_helper.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Code.require_file("support/fixture_scripts.ex", __DIR__)
44
Code.require_file("support/parity_matrix.ex", __DIR__)
55
Code.require_file("support/mock_websocket.ex", __DIR__)
66
Code.require_file("support/model_fixtures.ex", __DIR__)
7+
Code.require_file("support/temp_dir.ex", __DIR__)
78

89
loopback_available? =
910
case :gen_tcp.listen(0, [:binary, active: false, reuseaddr: true, ip: {127, 0, 0, 1}]) do

0 commit comments

Comments
 (0)