| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597 |
- // Copyright 2018 The go-ethereum Authors
- // This file is part of go-ethereum.
- //
- // go-ethereum is free software: you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // go-ethereum is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
- package main
- import (
- "bytes"
- "io/ioutil"
- "os"
- "path/filepath"
- "runtime"
- "testing"
- "github.com/ethereum/go-ethereum/swarm/api"
- swarm "github.com/ethereum/go-ethereum/swarm/api/client"
- swarmhttp "github.com/ethereum/go-ethereum/swarm/api/http"
- )
- // TestManifestChange tests manifest add, update and remove
- // cli commands without encryption.
- func TestManifestChange(t *testing.T) {
- if runtime.GOOS == "windows" {
- t.Skip()
- }
- testManifestChange(t, false)
- }
- // TestManifestChange tests manifest add, update and remove
- // cli commands with encryption enabled.
- func TestManifestChangeEncrypted(t *testing.T) {
- if runtime.GOOS == "windows" {
- t.Skip()
- }
- testManifestChange(t, true)
- }
- // testManifestChange performs cli commands:
- // - manifest add
- // - manifest update
- // - manifest remove
- // on a manifest, testing the functionality of this
- // comands on paths that are in root manifest or a nested one.
- // Argument encrypt controls whether to use encryption or not.
- func testManifestChange(t *testing.T, encrypt bool) {
- t.Parallel()
- srv := swarmhttp.NewTestSwarmServer(t, serverFunc, nil)
- defer srv.Close()
- tmp, err := ioutil.TempDir("", "swarm-manifest-test")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(tmp)
- origDir := filepath.Join(tmp, "orig")
- if err := os.Mkdir(origDir, 0777); err != nil {
- t.Fatal(err)
- }
- indexDataFilename := filepath.Join(origDir, "index.html")
- err = ioutil.WriteFile(indexDataFilename, []byte("<h1>Test</h1>"), 0666)
- if err != nil {
- t.Fatal(err)
- }
- // Files paths robots.txt and robots.html share the same prefix "robots."
- // which will result a manifest with a nested manifest under path "robots.".
- // This will allow testing manifest changes on both root and nested manifest.
- err = ioutil.WriteFile(filepath.Join(origDir, "robots.txt"), []byte("Disallow: /"), 0666)
- if err != nil {
- t.Fatal(err)
- }
- err = ioutil.WriteFile(filepath.Join(origDir, "robots.html"), []byte("<strong>No Robots Allowed</strong>"), 0666)
- if err != nil {
- t.Fatal(err)
- }
- err = ioutil.WriteFile(filepath.Join(origDir, "mutants.txt"), []byte("Frank\nMarcus"), 0666)
- if err != nil {
- t.Fatal(err)
- }
- args := []string{
- "--bzzapi",
- srv.URL,
- "--recursive",
- "--defaultpath",
- indexDataFilename,
- "up",
- origDir,
- }
- if encrypt {
- args = append(args, "--encrypt")
- }
- origManifestHash := runSwarmExpectHash(t, args...)
- checkHashLength(t, origManifestHash, encrypt)
- client := swarm.NewClient(srv.URL)
- // upload a new file and use its manifest to add it the original manifest.
- t.Run("add", func(t *testing.T) {
- humansData := []byte("Ann\nBob")
- humansDataFilename := filepath.Join(tmp, "humans.txt")
- err = ioutil.WriteFile(humansDataFilename, humansData, 0666)
- if err != nil {
- t.Fatal(err)
- }
- humansManifestHash := runSwarmExpectHash(t,
- "--bzzapi",
- srv.URL,
- "up",
- humansDataFilename,
- )
- newManifestHash := runSwarmExpectHash(t,
- "--bzzapi",
- srv.URL,
- "manifest",
- "add",
- origManifestHash,
- "humans.txt",
- humansManifestHash,
- )
- checkHashLength(t, newManifestHash, encrypt)
- newManifest := downloadManifest(t, client, newManifestHash, encrypt)
- var found bool
- for _, e := range newManifest.Entries {
- if e.Path == "humans.txt" {
- found = true
- if e.Size != int64(len(humansData)) {
- t.Errorf("expected humans.txt size %v, got %v", len(humansData), e.Size)
- }
- if e.ModTime.IsZero() {
- t.Errorf("got zero mod time for humans.txt")
- }
- ct := "text/plain; charset=utf-8"
- if e.ContentType != ct {
- t.Errorf("expected content type %q, got %q", ct, e.ContentType)
- }
- break
- }
- }
- if !found {
- t.Fatal("no humans.txt in new manifest")
- }
- checkFile(t, client, newManifestHash, "humans.txt", humansData)
- })
- // upload a new file and use its manifest to add it the original manifest,
- // but ensure that the file will be in the nested manifest of the original one.
- t.Run("add nested", func(t *testing.T) {
- robotsData := []byte(`{"disallow": "/"}`)
- robotsDataFilename := filepath.Join(tmp, "robots.json")
- err = ioutil.WriteFile(robotsDataFilename, robotsData, 0666)
- if err != nil {
- t.Fatal(err)
- }
- robotsManifestHash := runSwarmExpectHash(t,
- "--bzzapi",
- srv.URL,
- "up",
- robotsDataFilename,
- )
- newManifestHash := runSwarmExpectHash(t,
- "--bzzapi",
- srv.URL,
- "manifest",
- "add",
- origManifestHash,
- "robots.json",
- robotsManifestHash,
- )
- checkHashLength(t, newManifestHash, encrypt)
- newManifest := downloadManifest(t, client, newManifestHash, encrypt)
- var found bool
- loop:
- for _, e := range newManifest.Entries {
- if e.Path == "robots." {
- nestedManifest := downloadManifest(t, client, e.Hash, encrypt)
- for _, e := range nestedManifest.Entries {
- if e.Path == "json" {
- found = true
- if e.Size != int64(len(robotsData)) {
- t.Errorf("expected robots.json size %v, got %v", len(robotsData), e.Size)
- }
- if e.ModTime.IsZero() {
- t.Errorf("got zero mod time for robots.json")
- }
- ct := "application/json"
- if e.ContentType != ct {
- t.Errorf("expected content type %q, got %q", ct, e.ContentType)
- }
- break loop
- }
- }
- }
- }
- if !found {
- t.Fatal("no robots.json in new manifest")
- }
- checkFile(t, client, newManifestHash, "robots.json", robotsData)
- })
- // upload a new file and use its manifest to change the file it the original manifest.
- t.Run("update", func(t *testing.T) {
- indexData := []byte("<h1>Ethereum Swarm</h1>")
- indexDataFilename := filepath.Join(tmp, "index.html")
- err = ioutil.WriteFile(indexDataFilename, indexData, 0666)
- if err != nil {
- t.Fatal(err)
- }
- indexManifestHash := runSwarmExpectHash(t,
- "--bzzapi",
- srv.URL,
- "up",
- indexDataFilename,
- )
- newManifestHash := runSwarmExpectHash(t,
- "--bzzapi",
- srv.URL,
- "manifest",
- "update",
- origManifestHash,
- "index.html",
- indexManifestHash,
- )
- checkHashLength(t, newManifestHash, encrypt)
- newManifest := downloadManifest(t, client, newManifestHash, encrypt)
- var found bool
- for _, e := range newManifest.Entries {
- if e.Path == "index.html" {
- found = true
- if e.Size != int64(len(indexData)) {
- t.Errorf("expected index.html size %v, got %v", len(indexData), e.Size)
- }
- if e.ModTime.IsZero() {
- t.Errorf("got zero mod time for index.html")
- }
- ct := "text/html; charset=utf-8"
- if e.ContentType != ct {
- t.Errorf("expected content type %q, got %q", ct, e.ContentType)
- }
- break
- }
- }
- if !found {
- t.Fatal("no index.html in new manifest")
- }
- checkFile(t, client, newManifestHash, "index.html", indexData)
- // check default entry change
- checkFile(t, client, newManifestHash, "", indexData)
- })
- // upload a new file and use its manifest to change the file it the original manifest,
- // but ensure that the file is in the nested manifest of the original one.
- t.Run("update nested", func(t *testing.T) {
- robotsData := []byte(`<string>Only humans allowed!!!</strong>`)
- robotsDataFilename := filepath.Join(tmp, "robots.html")
- err = ioutil.WriteFile(robotsDataFilename, robotsData, 0666)
- if err != nil {
- t.Fatal(err)
- }
- humansManifestHash := runSwarmExpectHash(t,
- "--bzzapi",
- srv.URL,
- "up",
- robotsDataFilename,
- )
- newManifestHash := runSwarmExpectHash(t,
- "--bzzapi",
- srv.URL,
- "manifest",
- "update",
- origManifestHash,
- "robots.html",
- humansManifestHash,
- )
- checkHashLength(t, newManifestHash, encrypt)
- newManifest := downloadManifest(t, client, newManifestHash, encrypt)
- var found bool
- loop:
- for _, e := range newManifest.Entries {
- if e.Path == "robots." {
- nestedManifest := downloadManifest(t, client, e.Hash, encrypt)
- for _, e := range nestedManifest.Entries {
- if e.Path == "html" {
- found = true
- if e.Size != int64(len(robotsData)) {
- t.Errorf("expected robots.html size %v, got %v", len(robotsData), e.Size)
- }
- if e.ModTime.IsZero() {
- t.Errorf("got zero mod time for robots.html")
- }
- ct := "text/html; charset=utf-8"
- if e.ContentType != ct {
- t.Errorf("expected content type %q, got %q", ct, e.ContentType)
- }
- break loop
- }
- }
- }
- }
- if !found {
- t.Fatal("no robots.html in new manifest")
- }
- checkFile(t, client, newManifestHash, "robots.html", robotsData)
- })
- // remove a file from the manifest.
- t.Run("remove", func(t *testing.T) {
- newManifestHash := runSwarmExpectHash(t,
- "--bzzapi",
- srv.URL,
- "manifest",
- "remove",
- origManifestHash,
- "mutants.txt",
- )
- checkHashLength(t, newManifestHash, encrypt)
- newManifest := downloadManifest(t, client, newManifestHash, encrypt)
- var found bool
- for _, e := range newManifest.Entries {
- if e.Path == "mutants.txt" {
- found = true
- break
- }
- }
- if found {
- t.Fatal("mutants.txt is not removed")
- }
- })
- // remove a file from the manifest, but ensure that the file is in
- // the nested manifest of the original one.
- t.Run("remove nested", func(t *testing.T) {
- newManifestHash := runSwarmExpectHash(t,
- "--bzzapi",
- srv.URL,
- "manifest",
- "remove",
- origManifestHash,
- "robots.html",
- )
- checkHashLength(t, newManifestHash, encrypt)
- newManifest := downloadManifest(t, client, newManifestHash, encrypt)
- var found bool
- loop:
- for _, e := range newManifest.Entries {
- if e.Path == "robots." {
- nestedManifest := downloadManifest(t, client, e.Hash, encrypt)
- for _, e := range nestedManifest.Entries {
- if e.Path == "html" {
- found = true
- break loop
- }
- }
- }
- }
- if found {
- t.Fatal("robots.html in not removed")
- }
- })
- }
- // TestNestedDefaultEntryUpdate tests if the default entry is updated
- // if the file in nested manifest used for it is also updated.
- func TestNestedDefaultEntryUpdate(t *testing.T) {
- if runtime.GOOS == "windows" {
- t.Skip()
- }
- testNestedDefaultEntryUpdate(t, false)
- }
- // TestNestedDefaultEntryUpdateEncrypted tests if the default entry
- // of encrypted upload is updated if the file in nested manifest
- // used for it is also updated.
- func TestNestedDefaultEntryUpdateEncrypted(t *testing.T) {
- if runtime.GOOS == "windows" {
- t.Skip()
- }
- testNestedDefaultEntryUpdate(t, true)
- }
- func testNestedDefaultEntryUpdate(t *testing.T, encrypt bool) {
- t.Parallel()
- srv := swarmhttp.NewTestSwarmServer(t, serverFunc, nil)
- defer srv.Close()
- tmp, err := ioutil.TempDir("", "swarm-manifest-test")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(tmp)
- origDir := filepath.Join(tmp, "orig")
- if err := os.Mkdir(origDir, 0777); err != nil {
- t.Fatal(err)
- }
- indexData := []byte("<h1>Test</h1>")
- indexDataFilename := filepath.Join(origDir, "index.html")
- err = ioutil.WriteFile(indexDataFilename, indexData, 0666)
- if err != nil {
- t.Fatal(err)
- }
- // Add another file with common prefix as the default entry to test updates of
- // default entry with nested manifests.
- err = ioutil.WriteFile(filepath.Join(origDir, "index.txt"), []byte("Test"), 0666)
- if err != nil {
- t.Fatal(err)
- }
- args := []string{
- "--bzzapi",
- srv.URL,
- "--recursive",
- "--defaultpath",
- indexDataFilename,
- "up",
- origDir,
- }
- if encrypt {
- args = append(args, "--encrypt")
- }
- origManifestHash := runSwarmExpectHash(t, args...)
- checkHashLength(t, origManifestHash, encrypt)
- client := swarm.NewClient(srv.URL)
- newIndexData := []byte("<h1>Ethereum Swarm</h1>")
- newIndexDataFilename := filepath.Join(tmp, "index.html")
- err = ioutil.WriteFile(newIndexDataFilename, newIndexData, 0666)
- if err != nil {
- t.Fatal(err)
- }
- newIndexManifestHash := runSwarmExpectHash(t,
- "--bzzapi",
- srv.URL,
- "up",
- newIndexDataFilename,
- )
- newManifestHash := runSwarmExpectHash(t,
- "--bzzapi",
- srv.URL,
- "manifest",
- "update",
- origManifestHash,
- "index.html",
- newIndexManifestHash,
- )
- checkHashLength(t, newManifestHash, encrypt)
- newManifest := downloadManifest(t, client, newManifestHash, encrypt)
- var found bool
- for _, e := range newManifest.Entries {
- if e.Path == "index." {
- found = true
- newManifest = downloadManifest(t, client, e.Hash, encrypt)
- break
- }
- }
- if !found {
- t.Fatal("no index. path in new manifest")
- }
- found = false
- for _, e := range newManifest.Entries {
- if e.Path == "html" {
- found = true
- if e.Size != int64(len(newIndexData)) {
- t.Errorf("expected index.html size %v, got %v", len(newIndexData), e.Size)
- }
- if e.ModTime.IsZero() {
- t.Errorf("got zero mod time for index.html")
- }
- ct := "text/html; charset=utf-8"
- if e.ContentType != ct {
- t.Errorf("expected content type %q, got %q", ct, e.ContentType)
- }
- break
- }
- }
- if !found {
- t.Fatal("no html in new manifest")
- }
- checkFile(t, client, newManifestHash, "index.html", newIndexData)
- // check default entry change
- checkFile(t, client, newManifestHash, "", newIndexData)
- }
- func runSwarmExpectHash(t *testing.T, args ...string) (hash string) {
- t.Helper()
- hashRegexp := `[a-f\d]{64,128}`
- up := runSwarm(t, args...)
- _, matches := up.ExpectRegexp(hashRegexp)
- up.ExpectExit()
- if len(matches) < 1 {
- t.Fatal("no matches found")
- }
- return matches[0]
- }
- func checkHashLength(t *testing.T, hash string, encrypted bool) {
- t.Helper()
- l := len(hash)
- if encrypted && l != 128 {
- t.Errorf("expected hash length 128, got %v", l)
- }
- if !encrypted && l != 64 {
- t.Errorf("expected hash length 64, got %v", l)
- }
- }
- func downloadManifest(t *testing.T, client *swarm.Client, hash string, encrypted bool) (manifest *api.Manifest) {
- t.Helper()
- m, isEncrypted, err := client.DownloadManifest(hash)
- if err != nil {
- t.Fatal(err)
- }
- if encrypted != isEncrypted {
- t.Error("new manifest encryption flag is not correct")
- }
- return m
- }
- func checkFile(t *testing.T, client *swarm.Client, hash, path string, expected []byte) {
- t.Helper()
- f, err := client.Download(hash, path)
- if err != nil {
- t.Fatal(err)
- }
- got, err := ioutil.ReadAll(f)
- if err != nil {
- t.Fatal(err)
- }
- if !bytes.Equal(got, expected) {
- t.Errorf("expected file content %q, got %q", expected, got)
- }
- }
|