db_test.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. // Copyright (C) 2014 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 db
  7. import (
  8. "testing"
  9. "github.com/syncthing/syncthing/lib/db/backend"
  10. "github.com/syncthing/syncthing/lib/fs"
  11. "github.com/syncthing/syncthing/lib/protocol"
  12. )
  13. func genBlocks(n int) []protocol.BlockInfo {
  14. b := make([]protocol.BlockInfo, n)
  15. for i := range b {
  16. h := make([]byte, 32)
  17. for j := range h {
  18. h[j] = byte(i + j)
  19. }
  20. b[i].Size = int32(i)
  21. b[i].Hash = h
  22. }
  23. return b
  24. }
  25. func TestIgnoredFiles(t *testing.T) {
  26. ldb, err := openJSONS("testdata/v0.14.48-ignoredfiles.db.jsons")
  27. if err != nil {
  28. t.Fatal(err)
  29. }
  30. db := NewLowlevel(ldb)
  31. defer db.Close()
  32. if err := UpdateSchema(db); err != nil {
  33. t.Fatal(err)
  34. }
  35. fs := NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), db)
  36. // The contents of the database are like this:
  37. //
  38. // fs := NewFileSet("test", fs.NewFilesystem(fs.FilesystemTypeBasic, "."), db)
  39. // fs.Update(protocol.LocalDeviceID, []protocol.FileInfo{
  40. // { // invalid (ignored) file
  41. // Name: "foo",
  42. // Type: protocol.FileInfoTypeFile,
  43. // Invalid: true,
  44. // Version: protocol.Vector{Counters: []protocol.Counter{{ID: 1, Value: 1000}}},
  45. // },
  46. // { // regular file
  47. // Name: "bar",
  48. // Type: protocol.FileInfoTypeFile,
  49. // Version: protocol.Vector{Counters: []protocol.Counter{{ID: 1, Value: 1001}}},
  50. // },
  51. // })
  52. // fs.Update(protocol.DeviceID{42}, []protocol.FileInfo{
  53. // { // invalid file
  54. // Name: "baz",
  55. // Type: protocol.FileInfoTypeFile,
  56. // Invalid: true,
  57. // Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 1000}}},
  58. // },
  59. // { // regular file
  60. // Name: "quux",
  61. // Type: protocol.FileInfoTypeFile,
  62. // Version: protocol.Vector{Counters: []protocol.Counter{{ID: 42, Value: 1002}}},
  63. // },
  64. // })
  65. // Local files should have the "ignored" bit in addition to just being
  66. // generally invalid if we want to look at the simulation of that bit.
  67. snap := fs.Snapshot()
  68. defer snap.Release()
  69. fi, ok := snap.Get(protocol.LocalDeviceID, "foo")
  70. if !ok {
  71. t.Fatal("foo should exist")
  72. }
  73. if !fi.IsInvalid() {
  74. t.Error("foo should be invalid")
  75. }
  76. if !fi.IsIgnored() {
  77. t.Error("foo should be ignored")
  78. }
  79. fi, ok = snap.Get(protocol.LocalDeviceID, "bar")
  80. if !ok {
  81. t.Fatal("bar should exist")
  82. }
  83. if fi.IsInvalid() {
  84. t.Error("bar should not be invalid")
  85. }
  86. if fi.IsIgnored() {
  87. t.Error("bar should not be ignored")
  88. }
  89. // Remote files have the invalid bit as usual, and the IsInvalid() method
  90. // should pick this up too.
  91. fi, ok = snap.Get(protocol.DeviceID{42}, "baz")
  92. if !ok {
  93. t.Fatal("baz should exist")
  94. }
  95. if !fi.IsInvalid() {
  96. t.Error("baz should be invalid")
  97. }
  98. if !fi.IsInvalid() {
  99. t.Error("baz should be invalid")
  100. }
  101. fi, ok = snap.Get(protocol.DeviceID{42}, "quux")
  102. if !ok {
  103. t.Fatal("quux should exist")
  104. }
  105. if fi.IsInvalid() {
  106. t.Error("quux should not be invalid")
  107. }
  108. if fi.IsInvalid() {
  109. t.Error("quux should not be invalid")
  110. }
  111. }
  112. const myID = 1
  113. var (
  114. remoteDevice0, remoteDevice1 protocol.DeviceID
  115. update0to3Folder = "UpdateSchema0to3"
  116. invalid = "invalid"
  117. slashPrefixed = "/notgood"
  118. haveUpdate0to3 map[protocol.DeviceID]fileList
  119. )
  120. func init() {
  121. remoteDevice0, _ = protocol.DeviceIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR")
  122. remoteDevice1, _ = protocol.DeviceIDFromString("I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU")
  123. haveUpdate0to3 = map[protocol.DeviceID]fileList{
  124. protocol.LocalDeviceID: {
  125. protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
  126. protocol.FileInfo{Name: slashPrefixed, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
  127. },
  128. remoteDevice0: {
  129. protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)},
  130. protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), RawInvalid: true},
  131. protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)},
  132. },
  133. remoteDevice1: {
  134. protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)},
  135. protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), RawInvalid: true},
  136. protocol.FileInfo{Name: invalid, Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), RawInvalid: true},
  137. },
  138. }
  139. }
  140. func TestUpdate0to3(t *testing.T) {
  141. ldb, err := openJSONS("testdata/v0.14.45-update0to3.db.jsons")
  142. if err != nil {
  143. t.Fatal(err)
  144. }
  145. db := NewLowlevel(ldb)
  146. defer db.Close()
  147. updater := schemaUpdater{db}
  148. folder := []byte(update0to3Folder)
  149. if err := updater.updateSchema0to1(0); err != nil {
  150. t.Fatal(err)
  151. }
  152. trans, err := db.newReadOnlyTransaction()
  153. if err != nil {
  154. t.Fatal(err)
  155. }
  156. defer trans.Release()
  157. if _, ok, err := trans.getFile(folder, protocol.LocalDeviceID[:], []byte(slashPrefixed)); err != nil {
  158. t.Fatal(err)
  159. } else if ok {
  160. t.Error("File prefixed by '/' was not removed during transition to schema 1")
  161. }
  162. key, err := db.keyer.GenerateGlobalVersionKey(nil, folder, []byte(invalid))
  163. if err != nil {
  164. t.Fatal(err)
  165. }
  166. if _, err := db.Get(key); err != nil {
  167. t.Error("Invalid file wasn't added to global list")
  168. }
  169. if err := updater.updateSchema1to2(1); err != nil {
  170. t.Fatal(err)
  171. }
  172. found := false
  173. trans, err = db.newReadOnlyTransaction()
  174. if err != nil {
  175. t.Fatal(err)
  176. }
  177. defer trans.Release()
  178. _ = trans.withHaveSequence(folder, 0, func(fi FileIntf) bool {
  179. f := fi.(protocol.FileInfo)
  180. l.Infoln(f)
  181. if found {
  182. t.Error("Unexpected additional file via sequence", f.FileName())
  183. return true
  184. }
  185. if e := haveUpdate0to3[protocol.LocalDeviceID][0]; f.IsEquivalentOptional(e, 0, true, true, 0) {
  186. found = true
  187. } else {
  188. t.Errorf("Wrong file via sequence, got %v, expected %v", f, e)
  189. }
  190. return true
  191. })
  192. if !found {
  193. t.Error("Local file wasn't added to sequence bucket", err)
  194. }
  195. if err := updater.updateSchema2to3(2); err != nil {
  196. t.Fatal(err)
  197. }
  198. need := map[string]protocol.FileInfo{
  199. haveUpdate0to3[remoteDevice0][0].Name: haveUpdate0to3[remoteDevice0][0],
  200. haveUpdate0to3[remoteDevice1][0].Name: haveUpdate0to3[remoteDevice1][0],
  201. haveUpdate0to3[remoteDevice0][2].Name: haveUpdate0to3[remoteDevice0][2],
  202. }
  203. trans, err = db.newReadOnlyTransaction()
  204. if err != nil {
  205. t.Fatal(err)
  206. }
  207. defer trans.Release()
  208. _ = trans.withNeed(folder, protocol.LocalDeviceID[:], false, func(fi FileIntf) bool {
  209. e, ok := need[fi.FileName()]
  210. if !ok {
  211. t.Error("Got unexpected needed file:", fi.FileName())
  212. }
  213. f := fi.(protocol.FileInfo)
  214. delete(need, f.Name)
  215. if !f.IsEquivalentOptional(e, 0, true, true, 0) {
  216. t.Errorf("Wrong needed file, got %v, expected %v", f, e)
  217. }
  218. return true
  219. })
  220. for n := range need {
  221. t.Errorf(`Missing needed file "%v"`, n)
  222. }
  223. }
  224. func TestDowngrade(t *testing.T) {
  225. db := NewLowlevel(backend.OpenMemory())
  226. defer db.Close()
  227. // sets the min version etc
  228. if err := UpdateSchema(db); err != nil {
  229. t.Fatal(err)
  230. }
  231. // Bump the database version to something newer than we actually support
  232. miscDB := NewMiscDataNamespace(db)
  233. if err := miscDB.PutInt64("dbVersion", dbVersion+1); err != nil {
  234. t.Fatal(err)
  235. }
  236. l.Infoln(dbVersion)
  237. // Pretend we just opened the DB and attempt to update it again
  238. err := UpdateSchema(db)
  239. if err, ok := err.(databaseDowngradeError); !ok {
  240. t.Fatal("Expected error due to database downgrade, got", err)
  241. } else if err.minSyncthingVersion != dbMinSyncthingVersion {
  242. t.Fatalf("Error has %v as min Syncthing version, expected %v", err.minSyncthingVersion, dbMinSyncthingVersion)
  243. }
  244. }