github.com/codingfuture/orig-energi3@v0.8.4/cmd/swarm/manifest_test.go (about)

     1  // Copyright 2018 The Energi Core Authors
     2  // Copyright 2018 The go-ethereum Authors
     3  // This file is part of Energi Core.
     4  //
     5  // Energi Core is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // Energi Core is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU General Public License
    16  // along with Energi Core. If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package main
    19  
    20  import (
    21  	"bytes"
    22  	"io/ioutil"
    23  	"os"
    24  	"path/filepath"
    25  	"runtime"
    26  	"testing"
    27  
    28  	"github.com/ethereum/go-ethereum/swarm/api"
    29  	swarm "github.com/ethereum/go-ethereum/swarm/api/client"
    30  	swarmhttp "github.com/ethereum/go-ethereum/swarm/api/http"
    31  )
    32  
    33  // TestManifestChange tests manifest add, update and remove
    34  // cli commands without encryption.
    35  func TestManifestChange(t *testing.T) {
    36  	if runtime.GOOS == "windows" {
    37  		t.Skip()
    38  	}
    39  
    40  	testManifestChange(t, false)
    41  }
    42  
    43  // TestManifestChange tests manifest add, update and remove
    44  // cli commands with encryption enabled.
    45  func TestManifestChangeEncrypted(t *testing.T) {
    46  	if runtime.GOOS == "windows" {
    47  		t.Skip()
    48  	}
    49  
    50  	testManifestChange(t, true)
    51  }
    52  
    53  // testManifestChange performs cli commands:
    54  // - manifest add
    55  // - manifest update
    56  // - manifest remove
    57  // on a manifest, testing the functionality of this
    58  // comands on paths that are in root manifest or a nested one.
    59  // Argument encrypt controls whether to use encryption or not.
    60  func testManifestChange(t *testing.T, encrypt bool) {
    61  	t.Parallel()
    62  	srv := swarmhttp.NewTestSwarmServer(t, serverFunc, nil)
    63  	defer srv.Close()
    64  
    65  	tmp, err := ioutil.TempDir("", "swarm-manifest-test")
    66  	if err != nil {
    67  		t.Fatal(err)
    68  	}
    69  	defer os.RemoveAll(tmp)
    70  
    71  	origDir := filepath.Join(tmp, "orig")
    72  	if err := os.Mkdir(origDir, 0777); err != nil {
    73  		t.Fatal(err)
    74  	}
    75  
    76  	indexDataFilename := filepath.Join(origDir, "index.html")
    77  	err = ioutil.WriteFile(indexDataFilename, []byte("<h1>Test</h1>"), 0666)
    78  	if err != nil {
    79  		t.Fatal(err)
    80  	}
    81  	// Files paths robots.txt and robots.html share the same prefix "robots."
    82  	// which will result a manifest with a nested manifest under path "robots.".
    83  	// This will allow testing manifest changes on both root and nested manifest.
    84  	err = ioutil.WriteFile(filepath.Join(origDir, "robots.txt"), []byte("Disallow: /"), 0666)
    85  	if err != nil {
    86  		t.Fatal(err)
    87  	}
    88  	err = ioutil.WriteFile(filepath.Join(origDir, "robots.html"), []byte("<strong>No Robots Allowed</strong>"), 0666)
    89  	if err != nil {
    90  		t.Fatal(err)
    91  	}
    92  	err = ioutil.WriteFile(filepath.Join(origDir, "mutants.txt"), []byte("Frank\nMarcus"), 0666)
    93  	if err != nil {
    94  		t.Fatal(err)
    95  	}
    96  
    97  	args := []string{
    98  		"--bzzapi",
    99  		srv.URL,
   100  		"--recursive",
   101  		"--defaultpath",
   102  		indexDataFilename,
   103  		"up",
   104  		origDir,
   105  	}
   106  	if encrypt {
   107  		args = append(args, "--encrypt")
   108  	}
   109  
   110  	origManifestHash := runSwarmExpectHash(t, args...)
   111  
   112  	checkHashLength(t, origManifestHash, encrypt)
   113  
   114  	client := swarm.NewClient(srv.URL)
   115  
   116  	// upload a new file and use its manifest to add it the original manifest.
   117  	t.Run("add", func(t *testing.T) {
   118  		humansData := []byte("Ann\nBob")
   119  		humansDataFilename := filepath.Join(tmp, "humans.txt")
   120  		err = ioutil.WriteFile(humansDataFilename, humansData, 0666)
   121  		if err != nil {
   122  			t.Fatal(err)
   123  		}
   124  
   125  		humansManifestHash := runSwarmExpectHash(t,
   126  			"--bzzapi",
   127  			srv.URL,
   128  			"up",
   129  			humansDataFilename,
   130  		)
   131  
   132  		newManifestHash := runSwarmExpectHash(t,
   133  			"--bzzapi",
   134  			srv.URL,
   135  			"manifest",
   136  			"add",
   137  			origManifestHash,
   138  			"humans.txt",
   139  			humansManifestHash,
   140  		)
   141  
   142  		checkHashLength(t, newManifestHash, encrypt)
   143  
   144  		newManifest := downloadManifest(t, client, newManifestHash, encrypt)
   145  
   146  		var found bool
   147  		for _, e := range newManifest.Entries {
   148  			if e.Path == "humans.txt" {
   149  				found = true
   150  				if e.Size != int64(len(humansData)) {
   151  					t.Errorf("expected humans.txt size %v, got %v", len(humansData), e.Size)
   152  				}
   153  				if e.ModTime.IsZero() {
   154  					t.Errorf("got zero mod time for humans.txt")
   155  				}
   156  				ct := "text/plain; charset=utf-8"
   157  				if e.ContentType != ct {
   158  					t.Errorf("expected content type %q, got %q", ct, e.ContentType)
   159  				}
   160  				break
   161  			}
   162  		}
   163  		if !found {
   164  			t.Fatal("no humans.txt in new manifest")
   165  		}
   166  
   167  		checkFile(t, client, newManifestHash, "humans.txt", humansData)
   168  	})
   169  
   170  	// upload a new file and use its manifest to add it the original manifest,
   171  	// but ensure that the file will be in the nested manifest of the original one.
   172  	t.Run("add nested", func(t *testing.T) {
   173  		robotsData := []byte(`{"disallow": "/"}`)
   174  		robotsDataFilename := filepath.Join(tmp, "robots.json")
   175  		err = ioutil.WriteFile(robotsDataFilename, robotsData, 0666)
   176  		if err != nil {
   177  			t.Fatal(err)
   178  		}
   179  
   180  		robotsManifestHash := runSwarmExpectHash(t,
   181  			"--bzzapi",
   182  			srv.URL,
   183  			"up",
   184  			robotsDataFilename,
   185  		)
   186  
   187  		newManifestHash := runSwarmExpectHash(t,
   188  			"--bzzapi",
   189  			srv.URL,
   190  			"manifest",
   191  			"add",
   192  			origManifestHash,
   193  			"robots.json",
   194  			robotsManifestHash,
   195  		)
   196  
   197  		checkHashLength(t, newManifestHash, encrypt)
   198  
   199  		newManifest := downloadManifest(t, client, newManifestHash, encrypt)
   200  
   201  		var found bool
   202  	loop:
   203  		for _, e := range newManifest.Entries {
   204  			if e.Path == "robots." {
   205  				nestedManifest := downloadManifest(t, client, e.Hash, encrypt)
   206  				for _, e := range nestedManifest.Entries {
   207  					if e.Path == "json" {
   208  						found = true
   209  						if e.Size != int64(len(robotsData)) {
   210  							t.Errorf("expected robots.json size %v, got %v", len(robotsData), e.Size)
   211  						}
   212  						if e.ModTime.IsZero() {
   213  							t.Errorf("got zero mod time for robots.json")
   214  						}
   215  						ct := "application/json"
   216  						if e.ContentType != ct {
   217  							t.Errorf("expected content type %q, got %q", ct, e.ContentType)
   218  						}
   219  						break loop
   220  					}
   221  				}
   222  			}
   223  		}
   224  		if !found {
   225  			t.Fatal("no robots.json in new manifest")
   226  		}
   227  
   228  		checkFile(t, client, newManifestHash, "robots.json", robotsData)
   229  	})
   230  
   231  	// upload a new file and use its manifest to change the file it the original manifest.
   232  	t.Run("update", func(t *testing.T) {
   233  		indexData := []byte("<h1>Energi Swarm</h1>")
   234  		indexDataFilename := filepath.Join(tmp, "index.html")
   235  		err = ioutil.WriteFile(indexDataFilename, indexData, 0666)
   236  		if err != nil {
   237  			t.Fatal(err)
   238  		}
   239  
   240  		indexManifestHash := runSwarmExpectHash(t,
   241  			"--bzzapi",
   242  			srv.URL,
   243  			"up",
   244  			indexDataFilename,
   245  		)
   246  
   247  		newManifestHash := runSwarmExpectHash(t,
   248  			"--bzzapi",
   249  			srv.URL,
   250  			"manifest",
   251  			"update",
   252  			origManifestHash,
   253  			"index.html",
   254  			indexManifestHash,
   255  		)
   256  
   257  		checkHashLength(t, newManifestHash, encrypt)
   258  
   259  		newManifest := downloadManifest(t, client, newManifestHash, encrypt)
   260  
   261  		var found bool
   262  		for _, e := range newManifest.Entries {
   263  			if e.Path == "index.html" {
   264  				found = true
   265  				if e.Size != int64(len(indexData)) {
   266  					t.Errorf("expected index.html size %v, got %v", len(indexData), e.Size)
   267  				}
   268  				if e.ModTime.IsZero() {
   269  					t.Errorf("got zero mod time for index.html")
   270  				}
   271  				ct := "text/html; charset=utf-8"
   272  				if e.ContentType != ct {
   273  					t.Errorf("expected content type %q, got %q", ct, e.ContentType)
   274  				}
   275  				break
   276  			}
   277  		}
   278  		if !found {
   279  			t.Fatal("no index.html in new manifest")
   280  		}
   281  
   282  		checkFile(t, client, newManifestHash, "index.html", indexData)
   283  
   284  		// check default entry change
   285  		checkFile(t, client, newManifestHash, "", indexData)
   286  	})
   287  
   288  	// upload a new file and use its manifest to change the file it the original manifest,
   289  	// but ensure that the file is in the nested manifest of the original one.
   290  	t.Run("update nested", func(t *testing.T) {
   291  		robotsData := []byte(`<string>Only humans allowed!!!</strong>`)
   292  		robotsDataFilename := filepath.Join(tmp, "robots.html")
   293  		err = ioutil.WriteFile(robotsDataFilename, robotsData, 0666)
   294  		if err != nil {
   295  			t.Fatal(err)
   296  		}
   297  
   298  		humansManifestHash := runSwarmExpectHash(t,
   299  			"--bzzapi",
   300  			srv.URL,
   301  			"up",
   302  			robotsDataFilename,
   303  		)
   304  
   305  		newManifestHash := runSwarmExpectHash(t,
   306  			"--bzzapi",
   307  			srv.URL,
   308  			"manifest",
   309  			"update",
   310  			origManifestHash,
   311  			"robots.html",
   312  			humansManifestHash,
   313  		)
   314  
   315  		checkHashLength(t, newManifestHash, encrypt)
   316  
   317  		newManifest := downloadManifest(t, client, newManifestHash, encrypt)
   318  
   319  		var found bool
   320  	loop:
   321  		for _, e := range newManifest.Entries {
   322  			if e.Path == "robots." {
   323  				nestedManifest := downloadManifest(t, client, e.Hash, encrypt)
   324  				for _, e := range nestedManifest.Entries {
   325  					if e.Path == "html" {
   326  						found = true
   327  						if e.Size != int64(len(robotsData)) {
   328  							t.Errorf("expected robots.html size %v, got %v", len(robotsData), e.Size)
   329  						}
   330  						if e.ModTime.IsZero() {
   331  							t.Errorf("got zero mod time for robots.html")
   332  						}
   333  						ct := "text/html; charset=utf-8"
   334  						if e.ContentType != ct {
   335  							t.Errorf("expected content type %q, got %q", ct, e.ContentType)
   336  						}
   337  						break loop
   338  					}
   339  				}
   340  			}
   341  		}
   342  		if !found {
   343  			t.Fatal("no robots.html in new manifest")
   344  		}
   345  
   346  		checkFile(t, client, newManifestHash, "robots.html", robotsData)
   347  	})
   348  
   349  	// remove a file from the manifest.
   350  	t.Run("remove", func(t *testing.T) {
   351  		newManifestHash := runSwarmExpectHash(t,
   352  			"--bzzapi",
   353  			srv.URL,
   354  			"manifest",
   355  			"remove",
   356  			origManifestHash,
   357  			"mutants.txt",
   358  		)
   359  
   360  		checkHashLength(t, newManifestHash, encrypt)
   361  
   362  		newManifest := downloadManifest(t, client, newManifestHash, encrypt)
   363  
   364  		var found bool
   365  		for _, e := range newManifest.Entries {
   366  			if e.Path == "mutants.txt" {
   367  				found = true
   368  				break
   369  			}
   370  		}
   371  		if found {
   372  			t.Fatal("mutants.txt is not removed")
   373  		}
   374  	})
   375  
   376  	// remove a file from the manifest, but ensure that the file is in
   377  	// the nested manifest of the original one.
   378  	t.Run("remove nested", func(t *testing.T) {
   379  		newManifestHash := runSwarmExpectHash(t,
   380  			"--bzzapi",
   381  			srv.URL,
   382  			"manifest",
   383  			"remove",
   384  			origManifestHash,
   385  			"robots.html",
   386  		)
   387  
   388  		checkHashLength(t, newManifestHash, encrypt)
   389  
   390  		newManifest := downloadManifest(t, client, newManifestHash, encrypt)
   391  
   392  		var found bool
   393  	loop:
   394  		for _, e := range newManifest.Entries {
   395  			if e.Path == "robots." {
   396  				nestedManifest := downloadManifest(t, client, e.Hash, encrypt)
   397  				for _, e := range nestedManifest.Entries {
   398  					if e.Path == "html" {
   399  						found = true
   400  						break loop
   401  					}
   402  				}
   403  			}
   404  		}
   405  		if found {
   406  			t.Fatal("robots.html in not removed")
   407  		}
   408  	})
   409  }
   410  
   411  // TestNestedDefaultEntryUpdate tests if the default entry is updated
   412  // if the file in nested manifest used for it is also updated.
   413  func TestNestedDefaultEntryUpdate(t *testing.T) {
   414  	if runtime.GOOS == "windows" {
   415  		t.Skip()
   416  	}
   417  
   418  	testNestedDefaultEntryUpdate(t, false)
   419  }
   420  
   421  // TestNestedDefaultEntryUpdateEncrypted tests if the default entry
   422  // of encrypted upload is updated if the file in nested manifest
   423  // used for it is also updated.
   424  func TestNestedDefaultEntryUpdateEncrypted(t *testing.T) {
   425  	if runtime.GOOS == "windows" {
   426  		t.Skip()
   427  	}
   428  
   429  	testNestedDefaultEntryUpdate(t, true)
   430  }
   431  
   432  func testNestedDefaultEntryUpdate(t *testing.T, encrypt bool) {
   433  	t.Parallel()
   434  	srv := swarmhttp.NewTestSwarmServer(t, serverFunc, nil)
   435  	defer srv.Close()
   436  
   437  	tmp, err := ioutil.TempDir("", "swarm-manifest-test")
   438  	if err != nil {
   439  		t.Fatal(err)
   440  	}
   441  	defer os.RemoveAll(tmp)
   442  
   443  	origDir := filepath.Join(tmp, "orig")
   444  	if err := os.Mkdir(origDir, 0777); err != nil {
   445  		t.Fatal(err)
   446  	}
   447  
   448  	indexData := []byte("<h1>Test</h1>")
   449  	indexDataFilename := filepath.Join(origDir, "index.html")
   450  	err = ioutil.WriteFile(indexDataFilename, indexData, 0666)
   451  	if err != nil {
   452  		t.Fatal(err)
   453  	}
   454  	// Add another file with common prefix as the default entry to test updates of
   455  	// default entry with nested manifests.
   456  	err = ioutil.WriteFile(filepath.Join(origDir, "index.txt"), []byte("Test"), 0666)
   457  	if err != nil {
   458  		t.Fatal(err)
   459  	}
   460  
   461  	args := []string{
   462  		"--bzzapi",
   463  		srv.URL,
   464  		"--recursive",
   465  		"--defaultpath",
   466  		indexDataFilename,
   467  		"up",
   468  		origDir,
   469  	}
   470  	if encrypt {
   471  		args = append(args, "--encrypt")
   472  	}
   473  
   474  	origManifestHash := runSwarmExpectHash(t, args...)
   475  
   476  	checkHashLength(t, origManifestHash, encrypt)
   477  
   478  	client := swarm.NewClient(srv.URL)
   479  
   480  	newIndexData := []byte("<h1>Energi Swarm</h1>")
   481  	newIndexDataFilename := filepath.Join(tmp, "index.html")
   482  	err = ioutil.WriteFile(newIndexDataFilename, newIndexData, 0666)
   483  	if err != nil {
   484  		t.Fatal(err)
   485  	}
   486  
   487  	newIndexManifestHash := runSwarmExpectHash(t,
   488  		"--bzzapi",
   489  		srv.URL,
   490  		"up",
   491  		newIndexDataFilename,
   492  	)
   493  
   494  	newManifestHash := runSwarmExpectHash(t,
   495  		"--bzzapi",
   496  		srv.URL,
   497  		"manifest",
   498  		"update",
   499  		origManifestHash,
   500  		"index.html",
   501  		newIndexManifestHash,
   502  	)
   503  
   504  	checkHashLength(t, newManifestHash, encrypt)
   505  
   506  	newManifest := downloadManifest(t, client, newManifestHash, encrypt)
   507  
   508  	var found bool
   509  	for _, e := range newManifest.Entries {
   510  		if e.Path == "index." {
   511  			found = true
   512  			newManifest = downloadManifest(t, client, e.Hash, encrypt)
   513  			break
   514  		}
   515  	}
   516  	if !found {
   517  		t.Fatal("no index. path in new manifest")
   518  	}
   519  
   520  	found = false
   521  	for _, e := range newManifest.Entries {
   522  		if e.Path == "html" {
   523  			found = true
   524  			if e.Size != int64(len(newIndexData)) {
   525  				t.Errorf("expected index.html size %v, got %v", len(newIndexData), e.Size)
   526  			}
   527  			if e.ModTime.IsZero() {
   528  				t.Errorf("got zero mod time for index.html")
   529  			}
   530  			ct := "text/html; charset=utf-8"
   531  			if e.ContentType != ct {
   532  				t.Errorf("expected content type %q, got %q", ct, e.ContentType)
   533  			}
   534  			break
   535  		}
   536  	}
   537  	if !found {
   538  		t.Fatal("no html in new manifest")
   539  	}
   540  
   541  	checkFile(t, client, newManifestHash, "index.html", newIndexData)
   542  
   543  	// check default entry change
   544  	checkFile(t, client, newManifestHash, "", newIndexData)
   545  }
   546  
   547  func runSwarmExpectHash(t *testing.T, args ...string) (hash string) {
   548  	t.Helper()
   549  	hashRegexp := `[a-f\d]{64,128}`
   550  	up := runSwarm(t, args...)
   551  	_, matches := up.ExpectRegexp(hashRegexp)
   552  	up.ExpectExit()
   553  
   554  	if len(matches) < 1 {
   555  		t.Fatal("no matches found")
   556  	}
   557  	return matches[0]
   558  }
   559  
   560  func checkHashLength(t *testing.T, hash string, encrypted bool) {
   561  	t.Helper()
   562  	l := len(hash)
   563  	if encrypted && l != 128 {
   564  		t.Errorf("expected hash length 128, got %v", l)
   565  	}
   566  	if !encrypted && l != 64 {
   567  		t.Errorf("expected hash length 64, got %v", l)
   568  	}
   569  }
   570  
   571  func downloadManifest(t *testing.T, client *swarm.Client, hash string, encrypted bool) (manifest *api.Manifest) {
   572  	t.Helper()
   573  	m, isEncrypted, err := client.DownloadManifest(hash)
   574  	if err != nil {
   575  		t.Fatal(err)
   576  	}
   577  
   578  	if encrypted != isEncrypted {
   579  		t.Error("new manifest encryption flag is not correct")
   580  	}
   581  	return m
   582  }
   583  
   584  func checkFile(t *testing.T, client *swarm.Client, hash, path string, expected []byte) {
   585  	t.Helper()
   586  	f, err := client.Download(hash, path)
   587  	if err != nil {
   588  		t.Fatal(err)
   589  	}
   590  
   591  	got, err := ioutil.ReadAll(f)
   592  	if err != nil {
   593  		t.Fatal(err)
   594  	}
   595  	if !bytes.Equal(got, expected) {
   596  		t.Errorf("expected file content %q, got %q", expected, got)
   597  	}
   598  }