db_global_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. // Copyright (C) 2025 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package sqlite
  7. import (
  8. "slices"
  9. "testing"
  10. "github.com/syncthing/syncthing/lib/config"
  11. "github.com/syncthing/syncthing/lib/protocol"
  12. )
  13. func TestNeed(t *testing.T) {
  14. t.Helper()
  15. db, err := OpenTemp()
  16. if err != nil {
  17. t.Fatal(err)
  18. }
  19. t.Cleanup(func() {
  20. if err := db.Close(); err != nil {
  21. t.Fatal(err)
  22. }
  23. })
  24. // Some local files
  25. var v protocol.Vector
  26. baseV := v.Update(1)
  27. newerV := baseV.Update(42)
  28. files := []protocol.FileInfo{
  29. genFile("test1", 1, 0), // remote need
  30. genFile("test2", 2, 0), // local need
  31. genFile("test3", 3, 0), // global
  32. }
  33. files[0].Version = baseV
  34. files[1].Version = baseV
  35. files[2].Version = newerV
  36. err = db.Update(folderID, protocol.LocalDeviceID, files)
  37. if err != nil {
  38. t.Fatal(err)
  39. }
  40. // Some remote files
  41. remote := []protocol.FileInfo{
  42. genFile("test2", 2, 100), // global
  43. genFile("test3", 3, 101), // remote need
  44. genFile("test4", 4, 102), // local need
  45. }
  46. remote[0].Version = newerV
  47. remote[1].Version = baseV
  48. remote[2].Version = newerV
  49. err = db.Update(folderID, protocol.DeviceID{42}, remote)
  50. if err != nil {
  51. t.Fatal(err)
  52. }
  53. // A couple are needed locally
  54. localNeed := fiNames(mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 0, 0)))
  55. if !slices.Equal(localNeed, []string{"test2", "test4"}) {
  56. t.Log(localNeed)
  57. t.Fatal("bad local need")
  58. }
  59. // Another couple are needed remotely
  60. remoteNeed := fiNames(mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.DeviceID{42}, config.PullOrderAlphabetic, 0, 0)))
  61. if !slices.Equal(remoteNeed, []string{"test1", "test3"}) {
  62. t.Log(remoteNeed)
  63. t.Fatal("bad remote need")
  64. }
  65. }
  66. func TestDropRecalcsGlobal(t *testing.T) {
  67. // When we drop a device we may get a new global
  68. t.Parallel()
  69. t.Run("DropAllFiles", func(t *testing.T) {
  70. t.Parallel()
  71. testDropWithDropper(t, func(t *testing.T, db *DB) {
  72. t.Helper()
  73. if err := db.DropAllFiles(folderID, protocol.DeviceID{42}); err != nil {
  74. t.Fatal(err)
  75. }
  76. })
  77. })
  78. t.Run("DropDevice", func(t *testing.T) {
  79. t.Parallel()
  80. testDropWithDropper(t, func(t *testing.T, db *DB) {
  81. t.Helper()
  82. if err := db.DropDevice(protocol.DeviceID{42}); err != nil {
  83. t.Fatal(err)
  84. }
  85. })
  86. })
  87. t.Run("DropFilesNamed", func(t *testing.T) {
  88. t.Parallel()
  89. testDropWithDropper(t, func(t *testing.T, db *DB) {
  90. t.Helper()
  91. if err := db.DropFilesNamed(folderID, protocol.DeviceID{42}, []string{"test1", "test42"}); err != nil {
  92. t.Fatal(err)
  93. }
  94. })
  95. })
  96. }
  97. func testDropWithDropper(t *testing.T, dropper func(t *testing.T, db *DB)) {
  98. t.Helper()
  99. db, err := OpenTemp()
  100. if err != nil {
  101. t.Fatal(err)
  102. }
  103. t.Cleanup(func() {
  104. if err := db.Close(); err != nil {
  105. t.Fatal(err)
  106. }
  107. })
  108. // Some local files
  109. err = db.Update(folderID, protocol.LocalDeviceID, []protocol.FileInfo{
  110. genFile("test1", 1, 0),
  111. genFile("test2", 2, 0),
  112. })
  113. if err != nil {
  114. t.Fatal(err)
  115. }
  116. // Some remote files
  117. remote := []protocol.FileInfo{
  118. genFile("test1", 3, 0),
  119. }
  120. remote[0].Version = remote[0].Version.Update(42)
  121. err = db.Update(folderID, protocol.DeviceID{42}, remote)
  122. if err != nil {
  123. t.Fatal(err)
  124. }
  125. // Remote test1 wins as the global, verify.
  126. count, err := db.CountGlobal(folderID)
  127. if err != nil {
  128. t.Fatal(err)
  129. }
  130. if count.Bytes != (2+3)*128<<10 {
  131. t.Log(count)
  132. t.Fatal("bad global size to begin with")
  133. }
  134. if g, ok, err := db.GetGlobalFile(folderID, "test1"); err != nil || !ok {
  135. t.Fatal("missing global to begin with")
  136. } else if g.Size != 3*128<<10 {
  137. t.Fatal("remote test1 should be the global")
  138. }
  139. // Now remove that remote device
  140. dropper(t, db)
  141. // Our test1 should now be the global
  142. count, err = db.CountGlobal(folderID)
  143. if err != nil {
  144. t.Fatal(err)
  145. }
  146. if count.Bytes != (1+2)*128<<10 {
  147. t.Log(count)
  148. t.Fatal("bad global size after drop")
  149. }
  150. if g, ok, err := db.GetGlobalFile(folderID, "test1"); err != nil || !ok {
  151. t.Fatal("missing global after drop")
  152. } else if g.Size != 1*128<<10 {
  153. t.Fatal("local test1 should be the global")
  154. }
  155. }
  156. func TestNeedDeleted(t *testing.T) {
  157. t.Parallel()
  158. db, err := OpenTemp()
  159. if err != nil {
  160. t.Fatal(err)
  161. }
  162. t.Cleanup(func() {
  163. if err := db.Close(); err != nil {
  164. t.Fatal(err)
  165. }
  166. })
  167. // Some local files
  168. err = db.Update(folderID, protocol.LocalDeviceID, []protocol.FileInfo{
  169. genFile("test1", 1, 0),
  170. genFile("test2", 2, 0),
  171. })
  172. if err != nil {
  173. t.Fatal(err)
  174. }
  175. // A remote deleted file
  176. remote := []protocol.FileInfo{
  177. genFile("test1", 1, 101),
  178. }
  179. remote[0].SetDeleted(42)
  180. err = db.Update(folderID, protocol.DeviceID{42}, remote)
  181. if err != nil {
  182. t.Fatal(err)
  183. }
  184. // We need the one deleted file
  185. s, err := db.CountNeed(folderID, protocol.LocalDeviceID)
  186. if err != nil {
  187. t.Fatal(err)
  188. }
  189. if s.Bytes != 0 || s.Deleted != 1 {
  190. t.Log(s)
  191. t.Error("bad need")
  192. }
  193. }
  194. func TestDontNeedIgnored(t *testing.T) {
  195. t.Parallel()
  196. db, err := OpenTemp()
  197. if err != nil {
  198. t.Fatal(err)
  199. }
  200. t.Cleanup(func() {
  201. if err := db.Close(); err != nil {
  202. t.Fatal(err)
  203. }
  204. })
  205. // A remote file
  206. files := []protocol.FileInfo{
  207. genFile("test1", 1, 103),
  208. }
  209. err = db.Update(folderID, protocol.DeviceID{42}, files)
  210. if err != nil {
  211. t.Fatal(err)
  212. }
  213. // Which we've ignored locally
  214. files[0].SetIgnored()
  215. err = db.Update(folderID, protocol.LocalDeviceID, files)
  216. if err != nil {
  217. t.Fatal(err)
  218. }
  219. // We don't need it
  220. s, err := db.CountNeed(folderID, protocol.LocalDeviceID)
  221. if err != nil {
  222. t.Fatal(err)
  223. }
  224. if s.Bytes != 0 || s.Files != 0 {
  225. t.Log(s)
  226. t.Error("bad need")
  227. }
  228. // It shouldn't show up in the need list
  229. names := mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 0, 0))
  230. if len(names) != 0 {
  231. t.Log(names)
  232. t.Error("need no files")
  233. }
  234. }
  235. func TestRemoveDontNeedLocalIgnored(t *testing.T) {
  236. t.Parallel()
  237. db, err := OpenTemp()
  238. if err != nil {
  239. t.Fatal(err)
  240. }
  241. t.Cleanup(func() {
  242. if err := db.Close(); err != nil {
  243. t.Fatal(err)
  244. }
  245. })
  246. // A local ignored file
  247. file := genFile("test1", 1, 103)
  248. file.SetIgnored()
  249. files := []protocol.FileInfo{file}
  250. err = db.Update(folderID, protocol.LocalDeviceID, files)
  251. if err != nil {
  252. t.Fatal(err)
  253. }
  254. // Which the remote doesn't have (no update)
  255. // They don't need it
  256. s, err := db.CountNeed(folderID, protocol.DeviceID{42})
  257. if err != nil {
  258. t.Fatal(err)
  259. }
  260. if s.Bytes != 0 || s.Files != 0 {
  261. t.Log(s)
  262. t.Error("bad need")
  263. }
  264. // It shouldn't show up in their need list
  265. names := mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.DeviceID{42}, config.PullOrderAlphabetic, 0, 0))
  266. if len(names) != 0 {
  267. t.Log(names)
  268. t.Error("need no files")
  269. }
  270. }
  271. func TestLocalDontNeedDeletedMissing(t *testing.T) {
  272. t.Parallel()
  273. db, err := OpenTemp()
  274. if err != nil {
  275. t.Fatal(err)
  276. }
  277. t.Cleanup(func() {
  278. if err := db.Close(); err != nil {
  279. t.Fatal(err)
  280. }
  281. })
  282. // A remote deleted file
  283. file := genFile("test1", 1, 103)
  284. file.SetDeleted(42)
  285. files := []protocol.FileInfo{file}
  286. err = db.Update(folderID, protocol.DeviceID{42}, files)
  287. if err != nil {
  288. t.Fatal(err)
  289. }
  290. // Which we don't have (no local update)
  291. // We don't need it
  292. s, err := db.CountNeed(folderID, protocol.LocalDeviceID)
  293. if err != nil {
  294. t.Fatal(err)
  295. }
  296. if s.Bytes != 0 || s.Files != 0 || s.Deleted != 0 {
  297. t.Log(s)
  298. t.Error("bad need")
  299. }
  300. // It shouldn't show up in the need list
  301. names := mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 0, 0))
  302. if len(names) != 0 {
  303. t.Log(names)
  304. t.Error("need no files")
  305. }
  306. }
  307. func TestRemoteDontNeedDeletedMissing(t *testing.T) {
  308. t.Parallel()
  309. db, err := OpenTemp()
  310. if err != nil {
  311. t.Fatal(err)
  312. }
  313. t.Cleanup(func() {
  314. if err := db.Close(); err != nil {
  315. t.Fatal(err)
  316. }
  317. })
  318. // A local deleted file
  319. file := genFile("test1", 1, 103)
  320. file.SetDeleted(42)
  321. files := []protocol.FileInfo{file}
  322. err = db.Update(folderID, protocol.LocalDeviceID, files)
  323. if err != nil {
  324. t.Fatal(err)
  325. }
  326. // Which the remote doesn't have (no local update)
  327. // They don't need it
  328. s, err := db.CountNeed(folderID, protocol.DeviceID{42})
  329. if err != nil {
  330. t.Fatal(err)
  331. }
  332. if s.Bytes != 0 || s.Files != 0 || s.Deleted != 0 {
  333. t.Log(s)
  334. t.Error("bad need")
  335. }
  336. // It shouldn't show up in their need list
  337. names := mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.DeviceID{42}, config.PullOrderAlphabetic, 0, 0))
  338. if len(names) != 0 {
  339. t.Log(names)
  340. t.Error("need no files")
  341. }
  342. }
  343. func TestNeedRemoteSymlinkAndDir(t *testing.T) {
  344. t.Parallel()
  345. db, err := OpenTemp()
  346. if err != nil {
  347. t.Fatal(err)
  348. }
  349. t.Cleanup(func() {
  350. if err := db.Close(); err != nil {
  351. t.Fatal(err)
  352. }
  353. })
  354. // Two remote "specials", a symlink and a directory
  355. var v protocol.Vector
  356. v.Update(1)
  357. files := []protocol.FileInfo{
  358. {Name: "sym", Type: protocol.FileInfoTypeSymlink, Sequence: 100, Version: v, Blocks: genBlocks("symlink", 0, 1)},
  359. {Name: "dir", Type: protocol.FileInfoTypeDirectory, Sequence: 101, Version: v},
  360. }
  361. err = db.Update(folderID, protocol.DeviceID{42}, files)
  362. if err != nil {
  363. t.Fatal(err)
  364. }
  365. // We need them
  366. s, err := db.CountNeed(folderID, protocol.LocalDeviceID)
  367. if err != nil {
  368. t.Fatal(err)
  369. }
  370. if s.Directories != 1 || s.Symlinks != 1 {
  371. t.Log(s)
  372. t.Error("bad need")
  373. }
  374. // They should be in the need list
  375. names := mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 0, 0))
  376. if len(names) != 2 {
  377. t.Log(names)
  378. t.Error("bad need")
  379. }
  380. }
  381. func TestNeedPagination(t *testing.T) {
  382. t.Parallel()
  383. db, err := OpenTemp()
  384. if err != nil {
  385. t.Fatal(err)
  386. }
  387. t.Cleanup(func() {
  388. if err := db.Close(); err != nil {
  389. t.Fatal(err)
  390. }
  391. })
  392. // Several remote files
  393. var v protocol.Vector
  394. v.Update(1)
  395. files := []protocol.FileInfo{
  396. genFile("test0", 1, 100),
  397. genFile("test1", 1, 101),
  398. genFile("test2", 1, 102),
  399. genFile("test3", 1, 103),
  400. genFile("test4", 1, 104),
  401. genFile("test5", 1, 105),
  402. genFile("test6", 1, 106),
  403. genFile("test7", 1, 107),
  404. genFile("test8", 1, 108),
  405. genFile("test9", 1, 109),
  406. }
  407. err = db.Update(folderID, protocol.DeviceID{42}, files)
  408. if err != nil {
  409. t.Fatal(err)
  410. }
  411. // We should get the first two
  412. names := fiNames(mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 2, 0)))
  413. if !slices.Equal(names, []string{"test0", "test1"}) {
  414. t.Log(names)
  415. t.Error("bad need")
  416. }
  417. // We should get the next three
  418. names = fiNames(mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 3, 2)))
  419. if !slices.Equal(names, []string{"test2", "test3", "test4"}) {
  420. t.Log(names)
  421. t.Error("bad need")
  422. }
  423. // We should get the last five
  424. names = fiNames(mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 5, 5)))
  425. if !slices.Equal(names, []string{"test5", "test6", "test7", "test8", "test9"}) {
  426. t.Log(names)
  427. t.Error("bad need")
  428. }
  429. }