tree_test.go 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719
  1. /*
  2. * Rufs - Remote Union File System
  3. *
  4. * Copyright 2017 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the MIT
  7. * License, If a copy of the MIT License was not distributed with this
  8. * file, You can obtain one at https://opensource.org/licenses/MIT.
  9. */
  10. package rufs
  11. import (
  12. "bytes"
  13. "fmt"
  14. "io"
  15. "io/ioutil"
  16. "os"
  17. "strings"
  18. "testing"
  19. "devt.de/krotik/common/bitutil"
  20. "devt.de/krotik/common/errorutil"
  21. "devt.de/krotik/common/pools"
  22. "devt.de/krotik/rufs/config"
  23. "devt.de/krotik/rufs/node"
  24. )
  25. func TestRefresh(t *testing.T) {
  26. // Build up a tree from one branch
  27. cfg := map[string]interface{}{
  28. config.TreeSecret: "123",
  29. }
  30. tree, _ := NewTree(cfg, clientCert)
  31. branchRPC := fmt.Sprintf("%v:%v", branchConfigs["footest"][config.RPCHost], branchConfigs["footest"][config.RPCPort])
  32. if fp, err := tree.PingBranch("footest", branchRPC); err != nil || fp != footest.SSLFingerprint() {
  33. t.Error("Unexpected result:", fp, err)
  34. return
  35. }
  36. tree.AddBranch("footest", branchRPC, "")
  37. tree.AddMapping("/", "footest", true)
  38. paths, infos, err := tree.Dir("/", "", true, false)
  39. if res := DirResultToString(paths, infos); err != nil || res != `
  40. /
  41. drwxrwxrwx 4.0 KiB sub1
  42. -rw-rw-rw- 10 B test1
  43. -rw-rw-rw- 10 B test2
  44. /sub1
  45. -rw-rw-rw- 17 B test3
  46. `[1:] {
  47. t.Error("Unexpected result:", res, err)
  48. return
  49. }
  50. tree.Refresh()
  51. paths, infos, err = tree.Dir("/", "", true, false)
  52. if res := DirResultToString(paths, infos); err != nil || res != `
  53. /
  54. drwxrwxrwx 4.0 KiB sub1
  55. -rw-rw-rw- 10 B test1
  56. -rw-rw-rw- 10 B test2
  57. /sub1
  58. -rw-rw-rw- 17 B test3
  59. `[1:] {
  60. t.Error("Unexpected result:", res, err)
  61. return
  62. }
  63. // Simulate that the branch is no longer reachable
  64. tree.client.RemovePeer("footest")
  65. tree.client.RegisterPeer("footest", ":9095", "")
  66. if _, _, err = tree.Dir("/", "", true, false); err == nil {
  67. t.Error("Dir call should fail.")
  68. return
  69. }
  70. tree.Refresh()
  71. if res := tree.NotReachableBranches(); len(res) != 1 {
  72. t.Error("Unexpected result:", res)
  73. return
  74. }
  75. if res, _ := tree.ActiveBranches(); len(res) != 0 {
  76. t.Error("Unexpected result:", res)
  77. return
  78. }
  79. if res := tree.KnownBranches(); len(res) != 1 {
  80. t.Error("Unexpected result:", res)
  81. return
  82. }
  83. // Restore the branch again
  84. tree.client.RemovePeer("footest")
  85. tree.client.RegisterPeer("footest", branchRPC, "")
  86. tree.Refresh()
  87. paths, infos, err = tree.Dir("/", "", true, false)
  88. if res := DirResultToString(paths, infos); err != nil || res != `
  89. /
  90. drwxrwxrwx 4.0 KiB sub1
  91. -rw-rw-rw- 10 B test1
  92. -rw-rw-rw- 10 B test2
  93. /sub1
  94. -rw-rw-rw- 17 B test3
  95. `[1:] {
  96. t.Error("Unexpected result:", res, err)
  97. return
  98. }
  99. }
  100. func TestItemOpWrite(t *testing.T) {
  101. // Build up a tree from one branch
  102. cfg := map[string]interface{}{
  103. config.TreeSecret: "123",
  104. }
  105. tree, _ := NewTree(cfg, clientCert)
  106. branchRPC := fmt.Sprintf("%v:%v", branchConfigs["footest"][config.RPCHost], branchConfigs["footest"][config.RPCPort])
  107. if fp, err := tree.PingBranch("footest", branchRPC); err != nil || fp != footest.SSLFingerprint() {
  108. t.Error("Unexpected result:", fp, err)
  109. return
  110. }
  111. tree.AddBranch("footest", branchRPC, "")
  112. if res := fmt.Sprint(tree.ActiveBranches()); res != "[footest] ["+footest.SSLFingerprint()+"]" {
  113. t.Error("Unexpected result:", res)
  114. return
  115. }
  116. if err := tree.AddMapping("/", "bla", true); err == nil || err.Error() != "Unknown target node" {
  117. t.Error("Unexpected result:", err)
  118. return
  119. }
  120. tree.AddMapping("/", "footest", true)
  121. tree.AddMapping("/sub1", "footest", false)
  122. paths, infos, err := tree.Dir("/", "", true, false)
  123. if res := DirResultToString(paths, infos); err != nil || res != `
  124. /
  125. drwxrwxrwx 4.0 KiB sub1
  126. -rw-rw-rw- 10 B test1
  127. -rw-rw-rw- 10 B test2
  128. /sub1
  129. drwxrwxrwx 4.0 KiB sub1
  130. -rw-rw-rw- 10 B test1
  131. -rw-rw-rw- 10 B test2
  132. -rw-rw-rw- 17 B test3
  133. /sub1/sub1
  134. -rw-rw-rw- 17 B test3
  135. `[1:] {
  136. t.Error("Unexpected result:", res, err)
  137. return
  138. }
  139. // Make the byteSlicePool byte slices very small
  140. oldDefaultReadBufferSize := DefaultReadBufferSize
  141. defer func() {
  142. DefaultReadBufferSize = oldDefaultReadBufferSize
  143. byteSlicePool = pools.NewByteSlicePool(DefaultReadBufferSize)
  144. }()
  145. DefaultReadBufferSize = 3
  146. byteSlicePool = pools.NewByteSlicePool(DefaultReadBufferSize)
  147. bb := bytes.Buffer{}
  148. n, err := tree.WriteFile("/test99", []byte("Test"), 5)
  149. defer func() {
  150. // Make sure the written files are deleted
  151. footest.ItemOp("/", map[string]string{
  152. ItemOpAction: ItemOpActDelete,
  153. ItemOpName: "test99",
  154. })
  155. }()
  156. if err != nil || n != 4 {
  157. t.Error(n, err)
  158. return
  159. }
  160. if footest.ReadFileToBuffer("/test99", &bb); bb.String() !=
  161. string([]byte{0, 0, 0, 0, 0, 84, 101, 115, 116}) {
  162. t.Error("Unexpected result:", bb.Bytes())
  163. return
  164. }
  165. bb.Reset()
  166. n, err = footest.WriteFile("/test99", []byte("oo"), 1)
  167. if err != nil || n != 2 {
  168. t.Error(n, err)
  169. return
  170. }
  171. tree.WriteFile("/test99", []byte("oo"), 11)
  172. if footest.ReadFileToBuffer("/test99", &bb); bb.String() !=
  173. string([]byte{0, 111, 111, 0, 0, 84, 101, 115, 116, 0, 0, 111, 111}) {
  174. t.Error("Unexpected result:", bb.Bytes())
  175. return
  176. }
  177. bb.Reset()
  178. n, err = footest.WriteFile("/test98", []byte("Test"), 0)
  179. defer func() {
  180. // Make sure the written files are deleted
  181. footest.ItemOp("/", map[string]string{
  182. ItemOpAction: ItemOpActDelete,
  183. ItemOpName: "test98",
  184. })
  185. }()
  186. if err != nil || n != 4 {
  187. t.Error(n, err)
  188. return
  189. }
  190. if footest.ReadFileToBuffer("/test98", &bb); bb.String() != string("Test") {
  191. t.Error("Unexpected result:", bb.Bytes())
  192. return
  193. }
  194. bb.Reset()
  195. bb = *bytes.NewBuffer([]byte("Test buffer file"))
  196. err = footest.WriteFileFromBuffer("/test97", &bb)
  197. defer func() {
  198. // Make sure the written files are deleted
  199. footest.ItemOp("/", map[string]string{
  200. ItemOpAction: ItemOpActDelete,
  201. ItemOpName: "test97",
  202. })
  203. }()
  204. if err != nil {
  205. t.Error(err)
  206. return
  207. }
  208. if footest.ReadFileToBuffer("/test97", &bb); bb.String() != string("Test buffer file") {
  209. t.Error("Unexpected result:", bb.Bytes())
  210. return
  211. }
  212. bb.Reset()
  213. bb = *bytes.NewBuffer([]byte("Test buffer file2"))
  214. err = tree.WriteFileFromBuffer("/test96", &bb)
  215. defer func() {
  216. // Make sure the written files are deleted
  217. footest.ItemOp("/", map[string]string{
  218. ItemOpAction: ItemOpActDelete,
  219. ItemOpName: "test96",
  220. })
  221. }()
  222. if err != nil {
  223. t.Error(err)
  224. return
  225. }
  226. if footest.ReadFileToBuffer("/test96", &bb); bb.String() != string("Test buffer file2") {
  227. t.Error("Unexpected result:", bb.Bytes())
  228. return
  229. }
  230. bb.Reset()
  231. // Create an empty file
  232. tree.WriteFileFromBuffer("/test95", &bb)
  233. defer func() {
  234. // Make sure the written files are deleted
  235. footest.ItemOp("/", map[string]string{
  236. ItemOpAction: ItemOpActDelete,
  237. ItemOpName: "test95",
  238. })
  239. }()
  240. if res := dirLocal("./foo"); res != `
  241. sub1(dir)
  242. test1(10)
  243. test2(10)
  244. test95(0)
  245. test96(17)
  246. test97(16)
  247. test98(4)
  248. test99(13)
  249. `[1:] {
  250. t.Error("Unexpected result:", res)
  251. return
  252. }
  253. }
  254. func TestItemOpRead(t *testing.T) {
  255. // Build up a tree from two brances
  256. cfg := map[string]interface{}{
  257. config.TreeSecret: "123",
  258. }
  259. tree, _ := NewTree(cfg, clientCert)
  260. branchRPC := fmt.Sprintf("%v:%v", branchConfigs["footest"][config.RPCHost], branchConfigs["footest"][config.RPCPort])
  261. tree.AddBranch("footest", branchRPC, "")
  262. tree.AddMapping("/1", "footest", false)
  263. tree.AddMapping("/1/in/in", "footest", true)
  264. // We should now have the following structure:
  265. //
  266. // /1/test1
  267. // /1/test2
  268. // /1/sub1/test3
  269. // /1/in/in/test1
  270. // /1/in/in/test2
  271. // /1/in/in/sub1/test3
  272. paths, infos, err := tree.Dir("/", "", true, false)
  273. if res := DirResultToString(paths, infos); err != nil || res != `
  274. /
  275. drwxrwxrwx 0 B 1
  276. /1
  277. drwxrwxrwx 0 B in
  278. drwxrwxrwx 4.0 KiB sub1
  279. -rw-rw-rw- 10 B test1
  280. -rw-rw-rw- 10 B test2
  281. /1/in
  282. drwxrwxrwx 0 B in
  283. /1/in/in
  284. drwxrwxrwx 4.0 KiB sub1
  285. -rw-rw-rw- 10 B test1
  286. -rw-rw-rw- 10 B test2
  287. /1/in/in/sub1
  288. -rw-rw-rw- 17 B test3
  289. /1/sub1
  290. -rw-rw-rw- 17 B test3
  291. `[1:] {
  292. t.Error("Unexpected result:", res, err)
  293. return
  294. }
  295. paths, infos, err = tree.Dir("/1", "", false, false)
  296. if res := DirResultToString(paths, infos); err != nil || res != `
  297. /1
  298. drwxrwxrwx 0 B in
  299. drwxrwxrwx 4.0 KiB sub1
  300. -rw-rw-rw- 10 B test1
  301. -rw-rw-rw- 10 B test2
  302. `[1:] {
  303. t.Error("Unexpected result:", res, err)
  304. return
  305. }
  306. // Make the byteSlicePool byte slices very small
  307. oldDefaultReadBufferSize := DefaultReadBufferSize
  308. defer func() {
  309. DefaultReadBufferSize = oldDefaultReadBufferSize
  310. byteSlicePool = pools.NewByteSlicePool(DefaultReadBufferSize)
  311. }()
  312. DefaultReadBufferSize = 3
  313. byteSlicePool = pools.NewByteSlicePool(DefaultReadBufferSize)
  314. b := make([]byte, 20)
  315. n, err := tree.ReadFile("/1/test1", b, 0)
  316. if err != nil {
  317. t.Error(err)
  318. return
  319. }
  320. // Despite the small buffer size we should have read everything!
  321. if n != 10 || string(b[:n]) != "Test1 file" {
  322. t.Error("Unexpected result:", n, string(b[:n]))
  323. return
  324. }
  325. b = make([]byte, 3)
  326. n, err = tree.ReadFile("/1/test1", b, 0)
  327. if err != nil {
  328. t.Error(err)
  329. return
  330. }
  331. if n != 3 || string(b[:n]) != "Tes" {
  332. t.Error("Unexpected result:", n, string(b))
  333. return
  334. }
  335. // Try to read outside of root path
  336. _, err = tree.ReadFile("/1/../../test1", b, 0)
  337. if err == nil || err.Error() != "RufsError: Remote error (Requested path ../../test1 is outside of the branch)" {
  338. t.Error(err)
  339. return
  340. }
  341. // Do offset reading
  342. n, err = tree.ReadFile("/1/test1", b, 3)
  343. if err != nil {
  344. t.Error(err)
  345. return
  346. }
  347. if n != 3 || string(b[:n]) != "t1 " {
  348. t.Error("Unexpected result:", n, string(b))
  349. return
  350. }
  351. _, err = tree.ReadFile("/1/test1", b, 10)
  352. if err.(*node.Error).Detail != io.EOF.Error() {
  353. t.Error(err)
  354. return
  355. }
  356. byteSlicePool = pools.NewByteSlicePool(DefaultReadBufferSize)
  357. // Now read the same file using a reader
  358. bb := bytes.Buffer{}
  359. if err = tree.ReadFileToBuffer("/1/test2", &bb); err != nil {
  360. t.Error(err)
  361. return
  362. }
  363. if bb.String() != "Test2 file" {
  364. t.Error("Unexpected result: ", bb.String())
  365. return
  366. }
  367. bb.Reset()
  368. if err = footest.ReadFileToBuffer("test2", &bb); err != nil {
  369. t.Error(err)
  370. return
  371. }
  372. if bb.String() != "Test2 file" {
  373. t.Error("Unexpected result: ", bb.String())
  374. return
  375. }
  376. // Test error return
  377. _, err = tree.ReadFile("/1/sub1", []byte{0}, 0)
  378. if err == nil || err.Error() != "RufsError: Remote error (read /sub1: is a directory)" {
  379. t.Error("Unexpected result:", err)
  380. return
  381. }
  382. err = tree.ReadFileToBuffer("/1/sub1", &bb)
  383. if err == nil || err.Error() != "RufsError: Remote error (read /sub1: is a directory)" {
  384. t.Error("Unexpected result:", err)
  385. return
  386. }
  387. }
  388. func TestTreeDir(t *testing.T) {
  389. // Test the branch output
  390. paths, infos, err := footest.Dir("/", "", false, false)
  391. if res := DirResultToString(paths, infos); err != nil || res != `
  392. /
  393. drwxrwxrwx 4.0 KiB sub1
  394. -rw-rw-rw- 10 B test1
  395. -rw-rw-rw- 10 B test2
  396. `[1:] {
  397. t.Error("Unexpected result:", res, err)
  398. return
  399. }
  400. paths, infos, err = footest.Dir("/", "", true, false)
  401. if res := DirResultToString(paths, infos); err != nil || res != `
  402. /
  403. drwxrwxrwx 4.0 KiB sub1
  404. -rw-rw-rw- 10 B test1
  405. -rw-rw-rw- 10 B test2
  406. /sub1
  407. -rw-rw-rw- 17 B test3
  408. `[1:] {
  409. t.Error("Unexpected result:", res, err)
  410. return
  411. }
  412. // Build up a tree from one branch
  413. cfg := map[string]interface{}{
  414. config.TreeSecret: "123",
  415. }
  416. tree, _ := NewTree(cfg, clientCert)
  417. branchRPC := fmt.Sprintf("%v:%v", branchConfigs["footest"][config.RPCHost], branchConfigs["footest"][config.RPCPort])
  418. // Test errors for adding branches
  419. if err := tree.AddBranch("footest", branchRPC, "123"); err == nil ||
  420. !strings.HasPrefix(err.Error(), "Remote branch has an unexpected fingerprint") {
  421. t.Error(err)
  422. return
  423. }
  424. if err := tree.AddBranch("my", branchRPC, ""); err == nil ||
  425. err.Error() != "RufsError: Remote error (Unknown target node)" {
  426. t.Error(err)
  427. return
  428. }
  429. if err := tree.AddBranch("footest", branchRPC, ""); err != nil {
  430. t.Error(err)
  431. return
  432. }
  433. tree.AddMapping("/1", "footest", false)
  434. tree.AddMapping("/1/sub1", "footest", false)
  435. // We should now have the following structure:
  436. //
  437. // /1/test1
  438. // /1/test2
  439. // /1/sub1/test3
  440. // /1/sub1/test1
  441. // /1/sub1/test2
  442. // /1/sub1/sub1/test3
  443. paths, infos, err = tree.Dir("/", "", true, false)
  444. if res := DirResultToString(paths, infos); err != nil || res != `
  445. /
  446. drwxrwxrwx 0 B 1
  447. /1
  448. drwxrwxrwx 4.0 KiB sub1
  449. -rw-rw-rw- 10 B test1
  450. -rw-rw-rw- 10 B test2
  451. /1/sub1
  452. drwxrwxrwx 4.0 KiB sub1
  453. -rw-rw-rw- 10 B test1
  454. -rw-rw-rw- 10 B test2
  455. -rw-rw-rw- 17 B test3
  456. /1/sub1/sub1
  457. -rw-rw-rw- 17 B test3
  458. `[1:] {
  459. t.Error("Unexpected result:", res, err)
  460. return
  461. }
  462. // Add another mapping
  463. branchRPC = fmt.Sprintf("%v:%v", branchConfigs["bartest"][config.RPCHost], branchConfigs["bartest"][config.RPCPort])
  464. tree.AddBranch("bartest", branchRPC, "")
  465. tree.AddMapping("/2/sub2", "bartest", false)
  466. paths, infos, err = tree.Dir("/", "", true, false)
  467. if res := DirResultToString(paths, infos); err != nil || res != `
  468. /
  469. drwxrwxrwx 0 B 1
  470. drwxrwxrwx 0 B 2
  471. /1
  472. drwxrwxrwx 4.0 KiB sub1
  473. -rw-rw-rw- 10 B test1
  474. -rw-rw-rw- 10 B test2
  475. /1/sub1
  476. drwxrwxrwx 4.0 KiB sub1
  477. -rw-rw-rw- 10 B test1
  478. -rw-rw-rw- 10 B test2
  479. -rw-rw-rw- 17 B test3
  480. /1/sub1/sub1
  481. -rw-rw-rw- 17 B test3
  482. /2
  483. drwxrwxrwx 0 B sub2
  484. /2/sub2
  485. -rw-rw-rw- 10 B test1
  486. `[1:] {
  487. t.Error("Unexpected result:", res, err)
  488. return
  489. }
  490. // Now overlay a files - test1 of bartest should be overlaid by the
  491. // existing test1 from footest
  492. tree.AddMapping("/1", "bartest", false)
  493. paths, infos, err = tree.Dir("/", "", true, false)
  494. if res := DirResultToString(paths, infos); err != nil || res != `
  495. /
  496. drwxrwxrwx 0 B 1
  497. drwxrwxrwx 0 B 2
  498. /1
  499. drwxrwxrwx 4.0 KiB sub1
  500. -rw-rw-rw- 10 B test1
  501. -rw-rw-rw- 10 B test2
  502. /1/sub1
  503. drwxrwxrwx 4.0 KiB sub1
  504. -rw-rw-rw- 10 B test1
  505. -rw-rw-rw- 10 B test2
  506. -rw-rw-rw- 17 B test3
  507. /1/sub1/sub1
  508. -rw-rw-rw- 17 B test3
  509. /2
  510. drwxrwxrwx 0 B sub2
  511. /2/sub2
  512. -rw-rw-rw- 10 B test1
  513. `[1:] {
  514. t.Error("Unexpected result:", res, err)
  515. return
  516. }
  517. paths, infos, err = tree.Dir("/1", "", false, false)
  518. if res := DirResultToString(paths, infos); err != nil || res != `
  519. /1
  520. drwxrwxrwx 4.0 KiB sub1
  521. -rw-rw-rw- 10 B test1
  522. -rw-rw-rw- 10 B test2
  523. `[1:] {
  524. t.Error("Unexpected result:", res, err)
  525. return
  526. }
  527. paths, infos, err = tree.Dir("/1/sub1", "", false, false)
  528. if res := DirResultToString(paths, infos); err != nil || res != `
  529. /1/sub1
  530. drwxrwxrwx 4.0 KiB sub1
  531. -rw-rw-rw- 10 B test1
  532. -rw-rw-rw- 10 B test2
  533. -rw-rw-rw- 17 B test3
  534. `[1:] {
  535. t.Error("Unexpected result:", res, err)
  536. return
  537. }
  538. }
  539. func TestItemOpRename(t *testing.T) {
  540. // Build up a tree from one branch
  541. cfg := map[string]interface{}{
  542. config.TreeSecret: "123",
  543. }
  544. tree, _ := NewTree(cfg, clientCert)
  545. branchRPC := fmt.Sprintf("%v:%v", branchConfigs["footest"][config.RPCHost], branchConfigs["footest"][config.RPCPort])
  546. tree.AddBranch("footest", branchRPC, "")
  547. tree.AddMapping("/1", "footest", false)
  548. tree.AddMapping("/1/sub1", "footest", true)
  549. // We should now have the following structure:
  550. //
  551. // /1/test1
  552. // /1/test2
  553. // /1/sub1/test3
  554. // /1/sub1/test1
  555. // /1/sub1/test2
  556. // /1/sub1/sub1/test3
  557. if res := fmt.Sprint(tree); res != `
  558. /:
  559. 1/: footest(r)
  560. sub1/: footest(w)
  561. `[1:] {
  562. t.Error("Unexpected result:", res)
  563. return
  564. }
  565. paths, infos, err := footest.Dir("/", "", true, false)
  566. if res := DirResultToString(paths, infos); err != nil || res != `
  567. /
  568. drwxrwxrwx 4.0 KiB sub1
  569. -rw-rw-rw- 10 B test1
  570. -rw-rw-rw- 10 B test2
  571. /sub1
  572. -rw-rw-rw- 17 B test3
  573. `[1:] {
  574. t.Error("Unexpected result:", res, err)
  575. return
  576. }
  577. paths, infos, err = tree.Dir("/", "", true, false)
  578. if res := DirResultToString(paths, infos); err != nil || res != `
  579. /
  580. drwxrwxrwx 0 B 1
  581. /1
  582. drwxrwxrwx 4.0 KiB sub1
  583. -rw-rw-rw- 10 B test1
  584. -rw-rw-rw- 10 B test2
  585. /1/sub1
  586. drwxrwxrwx 4.0 KiB sub1
  587. -rw-rw-rw- 10 B test1
  588. -rw-rw-rw- 10 B test2
  589. -rw-rw-rw- 17 B test3
  590. /1/sub1/sub1
  591. -rw-rw-rw- 17 B test3
  592. `[1:] {
  593. t.Error("Unexpected result:", res, err)
  594. return
  595. }
  596. // Rename test1 to test111
  597. tree.ItemOp("/1/sub1", map[string]string{
  598. ItemOpAction: ItemOpActRename,
  599. ItemOpName: "test1",
  600. ItemOpNewName: "test111",
  601. })
  602. tree.ItemOp("/1/sub1", map[string]string{
  603. ItemOpAction: ItemOpActRename,
  604. ItemOpName: "sub1",
  605. ItemOpNewName: "sub99",
  606. })
  607. paths, infos, err = footest.Dir("/", "", true, false)
  608. if res := DirResultToString(paths, infos); err != nil || res != `
  609. /
  610. drwxrwxrwx 4.0 KiB sub99
  611. -rw-rw-rw- 10 B test111
  612. -rw-rw-rw- 10 B test2
  613. /sub99
  614. -rw-rw-rw- 17 B test3
  615. `[1:] {
  616. t.Error("Unexpected result:", res, err)
  617. return
  618. }
  619. paths, infos, err = tree.Dir("/", "", true, false)
  620. if res := DirResultToString(paths, infos); err != nil || res != `
  621. /
  622. drwxrwxrwx 0 B 1
  623. /1
  624. drwxrwxrwx 0 B sub1
  625. drwxrwxrwx 4.0 KiB sub99
  626. -rw-rw-rw- 10 B test111
  627. -rw-rw-rw- 10 B test2
  628. /1/sub1
  629. drwxrwxrwx 4.0 KiB sub99
  630. -rw-rw-rw- 10 B test111
  631. -rw-rw-rw- 10 B test2
  632. /1/sub1/sub99
  633. -rw-rw-rw- 17 B test3
  634. /1/sub99
  635. -rw-rw-rw- 17 B test3
  636. `[1:] {
  637. t.Error("Unexpected result:", res, err)
  638. return
  639. }
  640. // Rename test111 back to test1
  641. ok, err := tree.ItemOp("/1/sub1", map[string]string{
  642. ItemOpAction: ItemOpActRename,
  643. ItemOpName: "test111",
  644. ItemOpNewName: "test1",
  645. })
  646. if !ok || err != nil {
  647. t.Error(ok, err)
  648. return
  649. }
  650. ok, err = tree.ItemOp("/1/sub1", map[string]string{
  651. ItemOpAction: ItemOpActRename,
  652. ItemOpName: "sub99",
  653. ItemOpNewName: "sub1",
  654. })
  655. if !ok || err != nil {
  656. t.Error(ok, err)
  657. return
  658. }
  659. // Rename non existing file
  660. ok, err = tree.ItemOp("/1/sub1", map[string]string{
  661. ItemOpAction: ItemOpActRename,
  662. ItemOpName: "foobar",
  663. ItemOpNewName: "test99",
  664. })
  665. if ok || err == nil ||
  666. (err.Error() != "RufsError: Remote error (rename /foobar /test99: no such file or directory)" &&
  667. err.Error() != "RufsError: Remote error (rename \\foobar \\test99: The system cannot find the file specified.)") {
  668. t.Error(ok, err)
  669. return
  670. }
  671. paths, infos, err = footest.Dir("/", "", true, false)
  672. if res := DirResultToString(paths, infos); err != nil || res != `
  673. /
  674. drwxrwxrwx 4.0 KiB sub1
  675. -rw-rw-rw- 10 B test1
  676. -rw-rw-rw- 10 B test2
  677. /sub1
  678. -rw-rw-rw- 17 B test3
  679. `[1:] {
  680. t.Error("Unexpected result:", res, err)
  681. return
  682. }
  683. paths, infos, err = tree.Dir("/", "", true, false)
  684. if res := DirResultToString(paths, infos); err != nil || res != `
  685. /
  686. drwxrwxrwx 0 B 1
  687. /1
  688. drwxrwxrwx 4.0 KiB sub1
  689. -rw-rw-rw- 10 B test1
  690. -rw-rw-rw- 10 B test2
  691. /1/sub1
  692. drwxrwxrwx 4.0 KiB sub1
  693. -rw-rw-rw- 10 B test1
  694. -rw-rw-rw- 10 B test2
  695. -rw-rw-rw- 17 B test3
  696. /1/sub1/sub1
  697. -rw-rw-rw- 17 B test3
  698. `[1:] {
  699. t.Error("Unexpected result:", res, err)
  700. return
  701. }
  702. }
  703. func TestItemOpDelete(t *testing.T) {
  704. // Build up a tree from one branch
  705. cfg := map[string]interface{}{
  706. config.TreeSecret: "123",
  707. }
  708. tree, _ := NewTree(cfg, clientCert)
  709. branchRPC := fmt.Sprintf("%v:%v", branchConfigs["footest"][config.RPCHost], branchConfigs["footest"][config.RPCPort])
  710. tree.AddBranch("footest", branchRPC, "")
  711. branchRPC = fmt.Sprintf("%v:%v", branchConfigs["bartest"][config.RPCHost], branchConfigs["bartest"][config.RPCPort])
  712. tree.AddBranch("bartest", branchRPC, "")
  713. tree.AddMapping("/1", "footest", false)
  714. tree.AddMapping("/1/sub1", "footest", true)
  715. tree.AddMapping("///1///sub1", "bartest", false)
  716. conf := tree.Config()
  717. if conf != `{
  718. "branches": [
  719. {
  720. "branch": "footest",
  721. "fingerprint": "`+footest.SSLFingerprint()+`",
  722. "rpc": "localhost:9021"
  723. },
  724. {
  725. "branch": "bartest",
  726. "fingerprint": "`+bartest.SSLFingerprint()+`",
  727. "rpc": "localhost:9022"
  728. }
  729. ],
  730. "tree": [
  731. {
  732. "branch": "footest",
  733. "path": "/1",
  734. "writeable": false
  735. },
  736. {
  737. "branch": "footest",
  738. "path": "/1/sub1",
  739. "writeable": true
  740. },
  741. {
  742. "branch": "bartest",
  743. "path": "///1///sub1",
  744. "writeable": false
  745. }
  746. ]
  747. }` {
  748. t.Error("Unexpected config:", conf)
  749. return
  750. }
  751. if err := tree.SetMapping(conf); err != nil {
  752. t.Error(err)
  753. return
  754. }
  755. // We should now have the following structure:
  756. //
  757. // /1/test1
  758. // /1/test2
  759. // /1/sub1/test3
  760. // /1/sub1/test1
  761. // /1/sub1/test2
  762. // /1/sub1/sub1/test3
  763. if res := fmt.Sprint(tree); res != `
  764. /:
  765. 1/: footest(r)
  766. sub1/: footest(w), bartest(r)
  767. `[1:] {
  768. t.Error("Unexpected result:", res)
  769. return
  770. }
  771. // Create a new file
  772. ioutil.WriteFile("foo/test_to_delete", []byte("Test1 file"), 0770)
  773. paths, infos, err := tree.Dir("/", "", true, true)
  774. if res := DirResultToString(paths, infos); err != nil || res != `
  775. /
  776. drwxrwxrwx 0 B 1
  777. /1
  778. drwxrwxrwx 4.0 KiB sub1
  779. -rw-rw-rw- 10 B test1 [73b8af47]
  780. -rw-rw-rw- 10 B test2 [b0c1fadd]
  781. -rw-rw-rw- 10 B test_to_delete [73b8af47]
  782. /1/sub1
  783. drwxrwxrwx 4.0 KiB sub1
  784. -rw-rw-rw- 10 B test1 [73b8af47]
  785. -rw-rw-rw- 10 B test2 [b0c1fadd]
  786. -rw-rw-rw- 17 B test3 [f89782b1]
  787. -rw-rw-rw- 10 B test_to_delete [73b8af47]
  788. /1/sub1/sub1
  789. -rw-rw-rw- 17 B test3 [f89782b1]
  790. `[1:] {
  791. t.Error("Unexpected result:", res, err)
  792. return
  793. }
  794. // Delete file
  795. ok, err := tree.ItemOp("/1", map[string]string{
  796. ItemOpAction: ItemOpActDelete,
  797. ItemOpName: "test_to_delete",
  798. })
  799. if ok || err == nil || err.Error() != "All applicable branches for the requested path were mounted as not writable" {
  800. t.Error(ok, err)
  801. return
  802. }
  803. _, err = tree.WriteFile("/1/bla", []byte("test"), 0)
  804. if err == nil || err.Error() != "All applicable branches for the requested path were mounted as not writable" {
  805. t.Error(ok, err)
  806. return
  807. }
  808. ok, err = tree.ItemOp("/1/sub1", map[string]string{
  809. ItemOpAction: ItemOpActDelete,
  810. ItemOpName: "test_to_delete",
  811. })
  812. if !ok || err != nil {
  813. t.Error(ok, err)
  814. return
  815. }
  816. paths, infos, err = tree.Dir("/", "", true, true)
  817. if res := DirResultToString(paths, infos); err != nil || res != `
  818. /
  819. drwxrwxrwx 0 B 1
  820. /1
  821. drwxrwxrwx 4.0 KiB sub1
  822. -rw-rw-rw- 10 B test1 [73b8af47]
  823. -rw-rw-rw- 10 B test2 [b0c1fadd]
  824. /1/sub1
  825. drwxrwxrwx 4.0 KiB sub1
  826. -rw-rw-rw- 10 B test1 [73b8af47]
  827. -rw-rw-rw- 10 B test2 [b0c1fadd]
  828. -rw-rw-rw- 17 B test3 [f89782b1]
  829. /1/sub1/sub1
  830. -rw-rw-rw- 17 B test3 [f89782b1]
  831. `[1:] {
  832. t.Error("Unexpected result:", res, err)
  833. return
  834. }
  835. tree.Reset(false) // Just reset mappings not known branches
  836. tree.AddMapping("/1", "footest", false)
  837. tree.AddMapping("/1/sub1", "footest", true)
  838. tree.AddMapping("/1/sub1", "bartest", true)
  839. // Create new files to delete
  840. ioutil.WriteFile("foo/test_to_delete1", []byte("Test1 file"), 0770)
  841. ioutil.WriteFile("bar/test_to_delete2", []byte("Test1 file"), 0770)
  842. // Delete non existing file
  843. ok, err = tree.ItemOp("/1/sub1", map[string]string{
  844. ItemOpAction: ItemOpActDelete,
  845. ItemOpName: "test_to_delete3",
  846. })
  847. if err == nil || err.Error() != "RufsError: Remote error (file does not exist)" || ok {
  848. t.Error("Unexpected result:", ok, err)
  849. return
  850. }
  851. ok, err = tree.ItemOp("/1/sub1", map[string]string{
  852. ItemOpAction: ItemOpActDelete,
  853. ItemOpName: "",
  854. })
  855. if err == nil || err.Error() != "RufsError: Remote error (This operation requires a specific file or directory)" || ok {
  856. t.Error("Unexpected result:", ok, err)
  857. return
  858. }
  859. // Do a wildcard delete
  860. ok, err = tree.ItemOp("/1/sub1", map[string]string{
  861. ItemOpAction: ItemOpActDelete,
  862. ItemOpName: "test_to_delete*",
  863. })
  864. if !ok || err != nil {
  865. t.Error(ok, err)
  866. return
  867. }
  868. paths, infos, err = tree.Dir("/", "", true, true)
  869. if res := DirResultToString(paths, infos); err != nil || res != `
  870. /
  871. drwxrwxrwx 0 B 1
  872. /1
  873. drwxrwxrwx 4.0 KiB sub1
  874. -rw-rw-rw- 10 B test1 [73b8af47]
  875. -rw-rw-rw- 10 B test2 [b0c1fadd]
  876. /1/sub1
  877. drwxrwxrwx 4.0 KiB sub1
  878. -rw-rw-rw- 10 B test1 [73b8af47]
  879. -rw-rw-rw- 10 B test2 [b0c1fadd]
  880. -rw-rw-rw- 17 B test3 [f89782b1]
  881. /1/sub1/sub1
  882. -rw-rw-rw- 17 B test3 [f89782b1]
  883. `[1:] {
  884. t.Error("Unexpected result:", res, err)
  885. return
  886. }
  887. tree.Reset(true)
  888. paths, infos, err = tree.Dir("/", "", true, true)
  889. if res := DirResultToString(paths, infos); err != nil || res != `
  890. /
  891. `[1:] {
  892. t.Error("Unexpected result:", res, err)
  893. return
  894. }
  895. }
  896. func TestItemOpMkdir(t *testing.T) {
  897. // Build up a tree from one branch
  898. cfg := map[string]interface{}{
  899. config.TreeSecret: "123",
  900. }
  901. tree, _ := NewTree(cfg, clientCert)
  902. branchRPC := fmt.Sprintf("%v:%v", branchConfigs["footest"][config.RPCHost], branchConfigs["footest"][config.RPCPort])
  903. tree.AddBranch("footest", branchRPC, "")
  904. tree.AddMapping("/1", "footest", true)
  905. paths, infos, err := tree.Dir("/", "", true, true)
  906. if res := DirResultToString(paths, infos); err != nil || res != `
  907. /
  908. drwxrwxrwx 0 B 1
  909. /1
  910. drwxrwxrwx 4.0 KiB sub1
  911. -rw-rw-rw- 10 B test1 [73b8af47]
  912. -rw-rw-rw- 10 B test2 [b0c1fadd]
  913. /1/sub1
  914. -rw-rw-rw- 17 B test3 [f89782b1]
  915. `[1:] {
  916. t.Error("Unexpected result:", res, err)
  917. return
  918. }
  919. ok, err := tree.ItemOp("/1/sub1", map[string]string{
  920. ItemOpAction: ItemOpActMkDir,
  921. ItemOpName: "aaa/bbb",
  922. })
  923. if !ok || err != nil {
  924. t.Error(ok, err)
  925. return
  926. }
  927. paths, infos, err = tree.Dir("/", "", true, true)
  928. if res := DirResultToString(paths, infos); err != nil || res != `
  929. /
  930. drwxrwxrwx 0 B 1
  931. /1
  932. drwxrwxrwx 4.0 KiB sub1
  933. -rw-rw-rw- 10 B test1 [73b8af47]
  934. -rw-rw-rw- 10 B test2 [b0c1fadd]
  935. /1/sub1
  936. drwxrwxrwx 4.0 KiB bbb
  937. -rw-rw-rw- 17 B test3 [f89782b1]
  938. /1/sub1/bbb
  939. `[1:] {
  940. t.Error("Unexpected result:", res, err)
  941. return
  942. }
  943. errorutil.AssertOk(os.RemoveAll("./foo/sub1/bbb"))
  944. }
  945. func TestRelPath(t *testing.T) {
  946. if res := relPath("/1/", ""); res != "/1" {
  947. t.Error("Unexpected result:", res)
  948. return
  949. }
  950. if res := relPath("/1/", "/1/"); res != "/" {
  951. t.Error("Unexpected result:", res)
  952. return
  953. }
  954. if res := relPath("/1/test", "/1/"); res != "/test" {
  955. t.Error("Unexpected result:", res)
  956. return
  957. }
  958. if res := relPath("/1/test/", "/1"); res != "/test" {
  959. t.Error("Unexpected result:", res)
  960. return
  961. }
  962. }
  963. func TestSync(t *testing.T) {
  964. var buf bytes.Buffer
  965. // Build up a tree from one branch
  966. cfg := map[string]interface{}{
  967. config.TreeSecret: "123",
  968. }
  969. tree, _ := NewTree(cfg, clientCert)
  970. fooRPC := fmt.Sprintf("%v:%v", branchConfigs["footest"][config.RPCHost], branchConfigs["footest"][config.RPCPort])
  971. barRPC := fmt.Sprintf("%v:%v", branchConfigs["bartest"][config.RPCHost], branchConfigs["bartest"][config.RPCPort])
  972. errorutil.AssertOk(tree.AddBranch("footest", fooRPC, ""))
  973. errorutil.AssertOk(tree.AddBranch("bartest", barRPC, ""))
  974. errorutil.AssertOk(tree.AddMapping("/2", "footest", false))
  975. errorutil.AssertOk(tree.AddMapping("/3", "bartest", true))
  976. paths, infos, err := tree.Dir("/", "", true, true)
  977. if res := DirResultToString(paths, infos); err != nil || res != `
  978. /
  979. drwxrwxrwx 0 B 2
  980. drwxrwxrwx 0 B 3
  981. /2
  982. drwxrwxrwx 4.0 KiB sub1
  983. -rw-rw-rw- 10 B test1 [73b8af47]
  984. -rw-rw-rw- 10 B test2 [b0c1fadd]
  985. /2/sub1
  986. -rw-rw-rw- 17 B test3 [f89782b1]
  987. /3
  988. -rw-rw-rw- 10 B test1 [5b62da0f]
  989. `[1:] {
  990. t.Error("Unexpected result:", res, err)
  991. return
  992. }
  993. ioutil.WriteFile("./bar/test1", []byte("Test1 file"), 0770)
  994. ioutil.WriteFile("./bar/test2", []byte("Testx file"), 0770)
  995. ioutil.WriteFile("./bar/test5", []byte("Testx file"), 0770)
  996. os.Mkdir("./bar/sub2", 0755)
  997. ioutil.WriteFile("./bar/sub2/test2", []byte("Testx file"), 0770)
  998. ioutil.WriteFile("./foo/test3", []byte("Testx file"), 0770)
  999. ioutil.WriteFile("./foo/testempty", nil, 0770)
  1000. paths, infos, err = tree.Dir("/", "", true, true)
  1001. if res := DirResultToString(paths, infos); err != nil || res != `
  1002. /
  1003. drwxrwxrwx 0 B 2
  1004. drwxrwxrwx 0 B 3
  1005. /2
  1006. drwxrwxrwx 4.0 KiB sub1
  1007. -rw-rw-rw- 10 B test1 [73b8af47]
  1008. -rw-rw-rw- 10 B test2 [b0c1fadd]
  1009. -rw-rw-rw- 10 B test3 [91767c28]
  1010. -rw-rw-rw- 0 B testempty
  1011. /2/sub1
  1012. -rw-rw-rw- 17 B test3 [f89782b1]
  1013. /3
  1014. drwxrwxrwx 4.0 KiB sub2
  1015. -rw-rw-rw- 10 B test1 [73b8af47]
  1016. -rw-rw-rw- 10 B test2 [91767c28]
  1017. -rw-rw-rw- 10 B test5 [91767c28]
  1018. /3/sub2
  1019. -rw-rw-rw- 10 B test2 [91767c28]
  1020. `[1:] {
  1021. t.Error("Unexpected result:", res, err)
  1022. return
  1023. }
  1024. updFunc := func(op, srcFile, dstFile string, writtenBytes, totalBytes, currentFile, totalFiles int64) {
  1025. buf.WriteString(fmt.Sprintf("%v %v -> %v", op, srcFile, dstFile))
  1026. if writtenBytes > 0 {
  1027. buf.WriteString(" " + bitutil.ByteSizeString(writtenBytes, false))
  1028. buf.WriteString("/" + bitutil.ByteSizeString(totalBytes, false))
  1029. } else if writtenBytes == -1 {
  1030. buf.WriteString(" finished")
  1031. }
  1032. buf.WriteString("\n")
  1033. }
  1034. if err := tree.Sync("/2", "/3", false, updFunc); err != nil {
  1035. t.Error(buf.String(), err)
  1036. return
  1037. }
  1038. // Check log
  1039. if buf.String() != `
  1040. Create directory -> /3/sub1
  1041. Copy file /2/test2 -> /3/test2 10 B/10 B
  1042. Copy file /2/test2 -> /3/test2 finished
  1043. Copy file /2/test3 -> /3/test3 10 B/10 B
  1044. Copy file /2/test3 -> /3/test3 finished
  1045. Copy file /2/testempty -> /3/testempty
  1046. Remove directory -> /3/sub2
  1047. Remove file -> /3/test5
  1048. `[1:] {
  1049. t.Error("Unexpected log:", buf.String())
  1050. return
  1051. }
  1052. // Check new directory structure - Almost but not quite - sub1 in /3
  1053. // is empty as the call was not recursive
  1054. paths, infos, err = tree.Dir("/", "", true, true)
  1055. if res := DirResultToString(paths, infos); err != nil || res != `
  1056. /
  1057. drwxrwxrwx 0 B 2
  1058. drwxrwxrwx 0 B 3
  1059. /2
  1060. drwxrwxrwx 4.0 KiB sub1
  1061. -rw-rw-rw- 10 B test1 [73b8af47]
  1062. -rw-rw-rw- 10 B test2 [b0c1fadd]
  1063. -rw-rw-rw- 10 B test3 [91767c28]
  1064. -rw-rw-rw- 0 B testempty
  1065. /2/sub1
  1066. -rw-rw-rw- 17 B test3 [f89782b1]
  1067. /3
  1068. drwxrwxrwx 4.0 KiB sub1
  1069. -rw-rw-rw- 10 B test1 [73b8af47]
  1070. -rw-rw-rw- 10 B test2 [b0c1fadd]
  1071. -rw-rw-rw- 10 B test3 [91767c28]
  1072. -rw-rw-rw- 0 B testempty
  1073. /3/sub1
  1074. `[1:] {
  1075. t.Error("Unexpected result:", res, err)
  1076. return
  1077. }
  1078. // Now reset the whole thing and do it recursive this time!
  1079. os.RemoveAll("./bar/test3")
  1080. os.RemoveAll("./bar/sub1")
  1081. os.Mkdir("./bar/sub2", 0755)
  1082. ioutil.WriteFile("./bar/sub2/test2", []byte("Testx file"), 0770)
  1083. ioutil.WriteFile("./bar/test2", []byte("Testx file"), 0770)
  1084. buf.Reset()
  1085. paths, infos, err = tree.Dir("/", "", true, true)
  1086. if res := DirResultToString(paths, infos); err != nil || res != `
  1087. /
  1088. drwxrwxrwx 0 B 2
  1089. drwxrwxrwx 0 B 3
  1090. /2
  1091. drwxrwxrwx 4.0 KiB sub1
  1092. -rw-rw-rw- 10 B test1 [73b8af47]
  1093. -rw-rw-rw- 10 B test2 [b0c1fadd]
  1094. -rw-rw-rw- 10 B test3 [91767c28]
  1095. -rw-rw-rw- 0 B testempty
  1096. /2/sub1
  1097. -rw-rw-rw- 17 B test3 [f89782b1]
  1098. /3
  1099. drwxrwxrwx 4.0 KiB sub2
  1100. -rw-rw-rw- 10 B test1 [73b8af47]
  1101. -rw-rw-rw- 10 B test2 [91767c28]
  1102. -rw-rw-rw- 0 B testempty
  1103. /3/sub2
  1104. -rw-rw-rw- 10 B test2 [91767c28]
  1105. `[1:] {
  1106. t.Error("Unexpected result:", res, err)
  1107. return
  1108. }
  1109. if err := tree.Sync("/2", "/3", true, updFunc); err != nil {
  1110. t.Error(buf.String(), err)
  1111. return
  1112. }
  1113. if buf.String() != `
  1114. Create directory -> /3/sub1
  1115. Copy file /2/test2 -> /3/test2 10 B/10 B
  1116. Copy file /2/test2 -> /3/test2 finished
  1117. Copy file /2/test3 -> /3/test3 10 B/10 B
  1118. Copy file /2/test3 -> /3/test3 finished
  1119. Remove directory -> /3/sub2
  1120. Copy file /2/sub1/test3 -> /3/sub1/test3 17 B/17 B
  1121. Copy file /2/sub1/test3 -> /3/sub1/test3 finished
  1122. `[1:] {
  1123. t.Error("Unexpected log:", buf.String())
  1124. return
  1125. }
  1126. // Check new directory structure - Now sub1 in /3
  1127. // is not empty as the call was recursive
  1128. paths, infos, err = tree.Dir("/", "", true, true)
  1129. if res := DirResultToString(paths, infos); err != nil || res != `
  1130. /
  1131. drwxrwxrwx 0 B 2
  1132. drwxrwxrwx 0 B 3
  1133. /2
  1134. drwxrwxrwx 4.0 KiB sub1
  1135. -rw-rw-rw- 10 B test1 [73b8af47]
  1136. -rw-rw-rw- 10 B test2 [b0c1fadd]
  1137. -rw-rw-rw- 10 B test3 [91767c28]
  1138. -rw-rw-rw- 0 B testempty
  1139. /2/sub1
  1140. -rw-rw-rw- 17 B test3 [f89782b1]
  1141. /3
  1142. drwxrwxrwx 4.0 KiB sub1
  1143. -rw-rw-rw- 10 B test1 [73b8af47]
  1144. -rw-rw-rw- 10 B test2 [b0c1fadd]
  1145. -rw-rw-rw- 10 B test3 [91767c28]
  1146. -rw-rw-rw- 0 B testempty
  1147. /3/sub1
  1148. -rw-rw-rw- 17 B test3 [f89782b1]
  1149. `[1:] {
  1150. t.Error("Unexpected result:", res, err)
  1151. return
  1152. }
  1153. // Reset everything
  1154. ioutil.WriteFile("bar/test1", []byte("Test3 file"), 0770)
  1155. os.RemoveAll("./bar/test2")
  1156. os.RemoveAll("./bar/test3")
  1157. os.RemoveAll("./bar/sub1")
  1158. os.RemoveAll("./bar/testempty")
  1159. os.RemoveAll("./foo/test3")
  1160. os.RemoveAll("./foo/testempty")
  1161. // Test error reporting
  1162. buf.Reset()
  1163. if err := tree.Sync("/3", "/2", true, updFunc); err == nil || err.Error() != "All applicable branches for the requested path were mounted as not writable" {
  1164. t.Error(buf.String(), err)
  1165. return
  1166. }
  1167. // Make sure we "bomb out" after the first write attempt
  1168. if buf.String() != `
  1169. Copy file /3/test1 -> /2/test1
  1170. `[1:] {
  1171. t.Error("Unexpected log:", buf.String())
  1172. return
  1173. }
  1174. // Make sure everything is in the state it should be
  1175. if err := tree.CopyFile("/bla", "/3/xxx", nil); err == nil ||
  1176. err.Error() != "RufsError: Remote error (file does not exist)" {
  1177. t.Error("Unexpected result:", err)
  1178. return
  1179. }
  1180. paths, infos, err = tree.Dir("/", "", true, true)
  1181. if res := DirResultToString(paths, infos); err != nil || res != `
  1182. /
  1183. drwxrwxrwx 0 B 2
  1184. drwxrwxrwx 0 B 3
  1185. /2
  1186. drwxrwxrwx 4.0 KiB sub1
  1187. -rw-rw-rw- 10 B test1 [73b8af47]
  1188. -rw-rw-rw- 10 B test2 [b0c1fadd]
  1189. /2/sub1
  1190. -rw-rw-rw- 17 B test3 [f89782b1]
  1191. /3
  1192. -rw-rw-rw- 10 B test1 [5b62da0f]
  1193. `[1:] {
  1194. t.Error("Unexpected result:", res, err)
  1195. return
  1196. }
  1197. }
  1198. func TestDirPattern(t *testing.T) {
  1199. // Build up a tree from one branch
  1200. cfg := map[string]interface{}{
  1201. config.TreeSecret: "123",
  1202. }
  1203. tree, _ := NewTree(cfg, clientCert)
  1204. fooRPC := fmt.Sprintf("%v:%v", branchConfigs["footest"][config.RPCHost], branchConfigs["footest"][config.RPCPort])
  1205. barRPC := fmt.Sprintf("%v:%v", branchConfigs["bartest"][config.RPCHost], branchConfigs["bartest"][config.RPCPort])
  1206. errorutil.AssertOk(tree.AddBranch("footest", fooRPC, ""))
  1207. errorutil.AssertOk(tree.AddBranch("bartest", barRPC, ""))
  1208. errorutil.AssertOk(tree.AddMapping("/2", "footest", false))
  1209. errorutil.AssertOk(tree.AddMapping("/3", "bartest", true))
  1210. paths, infos, err := tree.Dir("/", "", true, true)
  1211. if res := DirResultToString(paths, infos); err != nil || res != `
  1212. /
  1213. drwxrwxrwx 0 B 2
  1214. drwxrwxrwx 0 B 3
  1215. /2
  1216. drwxrwxrwx 4.0 KiB sub1
  1217. -rw-rw-rw- 10 B test1 [73b8af47]
  1218. -rw-rw-rw- 10 B test2 [b0c1fadd]
  1219. /2/sub1
  1220. -rw-rw-rw- 17 B test3 [f89782b1]
  1221. /3
  1222. -rw-rw-rw- 10 B test1 [5b62da0f]
  1223. `[1:] {
  1224. t.Error("Unexpected result:", res, err)
  1225. return
  1226. }
  1227. paths, infos, err = tree.Dir("/", "2", true, true)
  1228. if res := DirResultToString(paths, infos); err != nil || res != `
  1229. /
  1230. drwxrwxrwx 0 B 2
  1231. /2
  1232. -rw-rw-rw- 10 B test2 [b0c1fadd]
  1233. /2/sub1
  1234. /3
  1235. `[1:] {
  1236. t.Error("Unexpected result:", res, err)
  1237. return
  1238. }
  1239. paths, infos, err = tree.Dir("/", "1", true, true)
  1240. if res := DirResultToString(paths, infos); err != nil || res != `
  1241. /
  1242. /2
  1243. drwxrwxrwx 4.0 KiB sub1
  1244. -rw-rw-rw- 10 B test1 [73b8af47]
  1245. /2/sub1
  1246. /3
  1247. -rw-rw-rw- 10 B test1 [5b62da0f]
  1248. `[1:] {
  1249. t.Error("Unexpected result:", res, err)
  1250. return
  1251. }
  1252. // Test error case
  1253. if _, _, err := tree.Dir("/", "(", true, true); err == nil || err.Error() != "error parsing regexp: missing closing ): `(`" {
  1254. t.Error("Unexpected result:", err)
  1255. return
  1256. }
  1257. }
  1258. func TestCopy(t *testing.T) {
  1259. var buf bytes.Buffer
  1260. // Build up a tree from one branch
  1261. cfg := map[string]interface{}{
  1262. config.TreeSecret: "123",
  1263. }
  1264. tree, _ := NewTree(cfg, clientCert)
  1265. fooRPC := fmt.Sprintf("%v:%v", branchConfigs["footest"][config.RPCHost], branchConfigs["footest"][config.RPCPort])
  1266. barRPC := fmt.Sprintf("%v:%v", branchConfigs["bartest"][config.RPCHost], branchConfigs["bartest"][config.RPCPort])
  1267. errorutil.AssertOk(tree.AddBranch("footest", fooRPC, ""))
  1268. errorutil.AssertOk(tree.AddBranch("bartest", barRPC, ""))
  1269. errorutil.AssertOk(tree.AddMapping("/2", "footest", false))
  1270. errorutil.AssertOk(tree.AddMapping("/3", "bartest", true))
  1271. paths, infos, err := tree.Dir("/", "", true, true)
  1272. if res := DirResultToString(paths, infos); err != nil || res != `
  1273. /
  1274. drwxrwxrwx 0 B 2
  1275. drwxrwxrwx 0 B 3
  1276. /2
  1277. drwxrwxrwx 4.0 KiB sub1
  1278. -rw-rw-rw- 10 B test1 [73b8af47]
  1279. -rw-rw-rw- 10 B test2 [b0c1fadd]
  1280. /2/sub1
  1281. -rw-rw-rw- 17 B test3 [f89782b1]
  1282. /3
  1283. -rw-rw-rw- 10 B test1 [5b62da0f]
  1284. `[1:] {
  1285. t.Error("Unexpected result:", res, err)
  1286. return
  1287. }
  1288. // Test stat
  1289. if fi, err := tree.Stat("/2/test1"); err != nil || fi.Name() != "test1" {
  1290. t.Error("Unexpected result:", fi, err)
  1291. return
  1292. }
  1293. if fi, err := tree.Stat("/2/test0"); err == nil || err.Error() != "RufsError: Remote error (file does not exist)" {
  1294. t.Error("Unexpected result:", fi, err)
  1295. return
  1296. }
  1297. updFunc := func(file string, writtenBytes, totalBytes, currentFile, totalFiles int64) {
  1298. buf.WriteString(fmt.Sprintf("%v %v/%v (%v of %v)\n", file, writtenBytes, totalBytes, currentFile, totalFiles))
  1299. }
  1300. if err := tree.Copy([]string{"/2", "/2/sub1", "/2/test1"}, "/3/4/5/6", updFunc); err != nil {
  1301. t.Error(buf.String(), err)
  1302. return
  1303. }
  1304. // Check the result
  1305. paths, infos, err = tree.Dir("/", "", true, true)
  1306. if res := DirResultToString(paths, infos); err != nil || res != `
  1307. /
  1308. drwxrwxrwx 0 B 2
  1309. drwxrwxrwx 0 B 3
  1310. /2
  1311. drwxrwxrwx 4.0 KiB sub1
  1312. -rw-rw-rw- 10 B test1 [73b8af47]
  1313. -rw-rw-rw- 10 B test2 [b0c1fadd]
  1314. /2/sub1
  1315. -rw-rw-rw- 17 B test3 [f89782b1]
  1316. /3
  1317. drwxrwxrwx 4.0 KiB 4
  1318. -rw-rw-rw- 10 B test1 [5b62da0f]
  1319. /3/4
  1320. drwxrwxrwx 4.0 KiB 5
  1321. /3/4/5
  1322. drwxrwxrwx 4.0 KiB 6
  1323. /3/4/5/6
  1324. drwxrwxrwx 4.0 KiB 2
  1325. drwxrwxrwx 4.0 KiB sub1
  1326. -rw-rw-rw- 10 B test1 [73b8af47]
  1327. /3/4/5/6/2
  1328. drwxrwxrwx 4.0 KiB sub1
  1329. -rw-rw-rw- 10 B test1 [73b8af47]
  1330. -rw-rw-rw- 10 B test2 [b0c1fadd]
  1331. /3/4/5/6/2/sub1
  1332. -rw-rw-rw- 17 B test3 [f89782b1]
  1333. /3/4/5/6/sub1
  1334. -rw-rw-rw- 17 B test3 [f89782b1]
  1335. `[1:] {
  1336. t.Error("Unexpected result:", res, err)
  1337. return
  1338. }
  1339. // Test error case
  1340. if err := tree.Copy([]string{"/2", "/2/test5"}, "/", updFunc); err == nil ||
  1341. err.Error() != "Cannot stat /2/test5: RufsError: Remote error (file does not exist)" {
  1342. t.Error(buf.String(), err)
  1343. return
  1344. }
  1345. if err := tree.Copy([]string{"/2", "/2/test1"}, "/", updFunc); err == nil ||
  1346. err.Error() != "Cannot copy /2/test1 to /: All applicable branches for the requested path were mounted as not writable" {
  1347. t.Error(buf.String(), err)
  1348. return
  1349. }
  1350. }