21
"github.com/stretchr/testify/require"
22
)
23
24
-// TestUpdate exercises the built-in "ipfs update" command tree against
25
-// the real GitHub Releases API. Network access is required.
24
+// TestUpdate exercises the built-in "ipfs update" command tree.
25
//
27
-// The node is created without Init or daemon, so install/revert error
28
-// paths that don't depend on a running daemon can be tested.
26
+// A local httptest server replaces GitHub Releases so the test does not
27
+// depend on network reachability or rate limits. The node is created
28
+// without Init or daemon, so install/revert error paths that don't
29
+// depend on a running daemon can be tested.
30
func TestUpdate(t *testing.T) {
31
t.Parallel()
32
h := harness.NewT(t)
33
node := h.NewNode()
34
35
+ srv := newMockGitHubReleases(t)
36
+ node.Runner.Env["TEST_KUBO_UPDATE_GITHUB_URL"] = srv.URL
37
+
38
t.Run("help text describes the command", func(t *testing.T) {
39
t.Parallel()
40
res := node.IPFS("update", "--help")
135
// (check, versions) work while the IPFS daemon holds the repo lock.
136
// These commands only query the GitHub API and never touch the repo,
137
// so they must succeed regardless of daemon state.
138
+//
139
+// A local httptest server replaces GitHub so the test does not depend
140
+// on network reachability or GitHub rate limits. The locking behavior
141
+// under test is independent of which endpoint serves the release JSON.
142
func TestUpdateWhileDaemonRuns(t *testing.T) {
143
t.Parallel()
136
- node := harness.NewT(t).NewNode().Init().StartDaemon()
144
+
145
+ srv := newMockGitHubReleases(t)
146
+ node := harness.NewT(t).NewNode()
147
+ node.Runner.Env["TEST_KUBO_UPDATE_GITHUB_URL"] = srv.URL
148
+ node.Init().StartDaemon()
149
defer node.StopDaemon()
150
151
t.Run("check succeeds with daemon running", func(t *testing.T) {
475
476
// --- test helpers ---
477
478
+// newMockGitHubReleases returns an httptest server that mimics the GitHub
479
+// Releases listing API with a single stable release at v0.99.0 carrying
480
+// a binary asset for the current GOOS/GOARCH. This is enough to drive
481
+// "ipfs update check" and "ipfs update versions" without touching the
482
+// real api.github.com.
483
+//
484
+// The asset name follows the same convention used by real kubo releases
485
+// (see https://github.com/ipfs/kubo/releases): kubo_<tag>_<os>-<arch>.<ext>,
486
+// where ext is "zip" on Windows and "tar.gz" everywhere else. This must
487
+// match what assetNameForPlatformTag produces in core/commands/update_github.go,
488
+// otherwise findReleaseAsset cannot locate the binary and reports
489
+// "no release found with a binary for <os>/<arch>".
490
+func newMockGitHubReleases(t *testing.T) *httptest.Server {
491
+ t.Helper()
492
+ ext := "tar.gz"
493
+ if runtime.GOOS == "windows" {
494
+ ext = "zip"
495
+ }
496
+ asset := fmt.Sprintf("kubo_v0.99.0_%s-%s.%s", runtime.GOOS, runtime.GOARCH, ext)
497
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
498
+ // Both `update check` and `update versions` call
499
+ // GET /releases?per_page=N. One stable release with a matching
500
+ // platform asset exercises both paths.
501
+ rels := []map[string]any{{
502
+ "tag_name": "v0.99.0",
503
+ "prerelease": false,
504
+ "assets": []map[string]any{{
505
+ "name": asset,
506
+ }},
507
+ }}
508
+ w.Header().Set("Content-Type", "application/json")
509
+ _ = json.NewEncoder(w).Encode(rels)
510
+ }))
511
+ t.Cleanup(srv.Close)
512
+ return srv
513
+}
514
+
515
// copyBuiltBinary copies the built ipfs binary (cmd/ipfs/ipfs) to dst.
516
// It locates the project root the same way the test harness does.
517
func copyBuiltBinary(t *testing.T, dst string) {