manifest_test.go 15 KB

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