- Split the monolith into package/provider/fetch/merge/cache modules with the Resolver deciding install/adopt/update/merge/conflict outcomes - Three-way merges via git merge-file/diff3 against a content-addressed merge-base cache; conflicts go to a .picopackage-merge sibling - Tests for package, provider, resolver, merge, and cache - notes.md: update UX and signing design — diff-by-default, SSH signature identity pinning (TOFU), key changes as a hard stop, exit-code contract - Add CLAUDE.md; remove the pre-refactor exe/pppkg monolith and scratch files Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EiyJC73Mz8xZyCTvCEY8qn
70 lines
2.6 KiB
Ruby
70 lines
2.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "test_helper"
|
|
|
|
# Two captured responses for the same gist, one revision apart: v2 prepends a
|
|
# `# Comments` line and carries a later `updated_at`. Real API shapes, no
|
|
# network, and enough of a difference to drive an update through the resolver.
|
|
class TestGithubGistProvider < Minitest::Test
|
|
include PicopackageTest
|
|
|
|
GIST_URL = "https://gist.github.com/dkam/a06926360de8edf108be8591368ce1fb"
|
|
|
|
StubFetcher = Struct.new(:body) do
|
|
def fetch(_uri) = body
|
|
end
|
|
|
|
def gist(revision)
|
|
Picopackage::GithubGistProvider.new(
|
|
GIST_URL, fetcher: StubFetcher.new(File.read("test/files/gist_#{revision}.json"))
|
|
)
|
|
end
|
|
|
|
def test_resolves_the_page_url_to_the_api
|
|
assert_equal "https://api.github.com/gists/a06926360de8edf108be8591368ce1fb",
|
|
gist("v1").url.to_s
|
|
end
|
|
|
|
def test_extracts_the_file_from_the_json_envelope
|
|
package = gist("v1").package
|
|
|
|
assert_equal "ipv6_in_sqlite.rb", package.filename
|
|
assert_includes package.payload, "def ipv6"
|
|
refute_includes package.payload, "raw_url", "the JSON envelope must not leak into the payload"
|
|
assert_predicate package, :bare?, "the gist carries no metadata block of its own"
|
|
end
|
|
|
|
# The gist's own revision time, not the moment we happened to fetch it.
|
|
def test_uses_the_gists_updated_at_as_the_timestamp
|
|
assert_equal "2025-01-25T00:07:09Z", gist("v1").package.payload_timestamp
|
|
assert_equal "2025-01-25T00:08:48Z", gist("v2").package.payload_timestamp
|
|
end
|
|
|
|
def test_a_new_revision_is_a_different_payload
|
|
refute_equal gist("v1").package.payload_digest, gist("v2").package.payload_digest
|
|
end
|
|
|
|
# End to end against a real upstream shape: install v1, edit it, take v2.
|
|
def test_updating_across_revisions_keeps_local_edits
|
|
with_dirs do |_upstream, project, cache|
|
|
local_path = File.join(project, "ipv6_in_sqlite.rb")
|
|
assert_equal :installed, resolve(gist("v1").package, nil, local_path, cache).state
|
|
|
|
File.write(local_path, File.read(local_path).sub("where(ipv6_high:", "where!(ipv6_high:"))
|
|
result = resolve(gist("v2").package, Picopackage::Package.from_file(local_path), local_path, cache)
|
|
|
|
assert_equal :merged, result.state, result.message
|
|
merged = File.read(local_path)
|
|
assert_includes merged, "where!(ipv6_high:", "local edit should survive"
|
|
assert_includes merged, "# Comments", "the new revision should be applied"
|
|
refute_includes merged, "<<<<<<<"
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def resolve(remote, local, path, cache)
|
|
Picopackage::Resolver.new(remote, local, path, cache: cache).resolve
|
|
end
|
|
end
|