cc-os/plugins/os-backlog/tests/project_index_test.rb

133 lines
5.0 KiB
Ruby
Raw Permalink Normal View History

require_relative "test_helper"
require "tmpdir"
require "fileutils"
require "json"
class ProjectIndexTest < Minitest::Test
def with_index
Dir.mktmpdir do |dir|
path = File.join(dir, ".cc-os", "projects.json")
yield Backlog::ProjectIndex.new(path: path, today: -> { "2026-07-13" }), path
end
end
def test_missing_file_reads_as_empty
with_index do |index, _path|
assert_equal({}, index.all)
end
end
def test_corrupt_file_reads_as_empty_and_is_not_clobbered_by_reads
with_index do |index, path|
FileUtils.mkdir_p(File.dirname(path))
File.write(path, "{not json")
assert_equal({}, index.all)
assert_equal "{not json", File.read(path) # read paths never rewrite
end
end
def test_upsert_creates_row_with_updated_at
with_index do |index, path|
index.upsert(path: "/repos/foo", name: "foo", tracker: "planka:foo", remote: nil)
data = JSON.parse(File.read(path))
assert_equal 1, data["version"]
row = data["projects"]["/repos/foo"]
assert_equal "foo", row["name"]
assert_equal "planka:foo", row["tracker"]
assert_nil row["remote"]
assert_equal "2026-07-13", row["updated_at"]
end
end
def test_upsert_updates_existing_row_keyed_by_path
with_index do |index, _path|
index.upsert(path: "/repos/foo", name: "foo", tracker: "planka:foo", remote: nil)
index.upsert(path: "/repos/foo", name: "foo", tracker: "github:me/foo", remote: "r")
assert_equal 1, index.all.size
assert_equal "github:me/foo", index.all["/repos/foo"]["tracker"]
end
end
def test_name_collision_two_paths_same_basename_keeps_both
with_index do |index, _path|
index.upsert(path: "/a/foo", name: "foo", tracker: "planka:foo", remote: nil)
index.upsert(path: "/b/foo", name: "foo", tracker: "github:me/foo", remote: nil)
assert_equal %w[/a/foo /b/foo], index.all.keys.sort
end
end
def test_upsert_over_corrupt_file_leaves_valid_json_and_no_temp_files
with_index do |index, path|
FileUtils.mkdir_p(File.dirname(path))
File.write(path, "{not json")
index.upsert(path: "/repos/foo", name: "foo", tracker: "repo:.", remote: nil)
data = JSON.parse(File.read(path))
assert_equal ["/repos/foo"], data["projects"].keys
leftovers = Dir.children(File.dirname(path)).reject { |f| f == "projects.json" }
assert_empty leftovers, "atomic write left temp files: #{leftovers}"
end
end
def test_default_path_honors_cc_os_home_override
path = Backlog::ProjectIndex.default_path(env: { "CC_OS_HOME" => "/tmp/x", "HOME" => "/home/y" })
assert_equal "/tmp/x/projects.json", path
end
def test_default_path_falls_back_to_home
path = Backlog::ProjectIndex.default_path(env: { "HOME" => "/home/y" })
assert_equal "/home/y/.cc-os/projects.json", path
end
end
class ProjectsCliContractTest < Minitest::Test
BIN = File.expand_path("../bin/os-backlog", __dir__)
# config-write must upsert the index (redirected via CC_OS_HOME so the
# real ~/.cc-os is never touched), and `projects` must always emit a
# top-level "projects" key — even when the index is empty.
def test_config_write_upserts_index_and_projects_lists_it
Dir.mktmpdir do |home|
Dir.mktmpdir do |repo|
env = "CC_OS_HOME=#{home} HOME=#{home}"
out = Dir.chdir(repo) { `#{env} ruby #{BIN} projects 2>/dev/null` }
assert_equal({ "projects" => {} }, JSON.parse(out))
Dir.chdir(repo) { `#{env} ruby #{BIN} config-write planka:demo 2>/dev/null` }
out = Dir.chdir(repo) { `#{env} ruby #{BIN} projects 2>/dev/null` }
projects = JSON.parse(out)["projects"]
assert_equal 1, projects.size
row = projects.values.first
assert_equal "planka:demo", row["tracker"]
assert_equal File.basename(File.realpath(repo)), row["name"]
filtered = Dir.chdir(repo) { `#{env} ruby #{BIN} projects no-such-name 2>/dev/null` }
assert_equal({}, JSON.parse(filtered)["projects"])
by_tracker = Dir.chdir(repo) { `#{env} ruby #{BIN} projects planka:demo 2>/dev/null` }
assert_equal 1, JSON.parse(by_tracker)["projects"].size
end
end
end
# Umbrella repos: config-write from a subdirectory writes the config to
# that subdirectory, so the index row must be keyed by the SUBDIRECTORY
# (the project the config describes), never the enclosing git toplevel —
# otherwise every subproject masquerades as the umbrella repo.
def test_config_write_in_umbrella_subdir_indexes_the_subdir
Dir.mktmpdir do |home|
Dir.mktmpdir do |repo|
`git -C #{repo} init -q 2>/dev/null`
subdir = File.join(repo, "child-project")
FileUtils.mkdir_p(subdir)
env = "CC_OS_HOME=#{home} HOME=#{home}"
Dir.chdir(subdir) { `#{env} ruby #{BIN} config-write planka:child 2>/dev/null` }
projects = Dir.chdir(subdir) { JSON.parse(`#{env} ruby #{BIN} projects 2>/dev/null`)["projects"] }
assert_equal [File.realpath(subdir)], projects.keys
assert_equal "child-project", projects.values.first["name"]
end
end
end
end