storage_test.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
  1. package storage
  2. import (
  3. "database/sql"
  4. "errors"
  5. "log"
  6. "testing"
  7. _ "github.com/mattn/go-sqlite3"
  8. "github.com/stretchr/testify/assert"
  9. )
  10. const badPostsTable = `
  11. CREATE TABLE IF NOT EXISTS posts(
  12. row INTEGER PRIMARY KEY AUTOINCREMENT
  13. );
  14. `
  15. const badImagesTable = `
  16. CREATE TABLE IF NOT EXISTS images(
  17. row INTEGER PRIMARY KEY AUTOINCREMENT
  18. );
  19. `
  20. const badMenuItemsTable = `
  21. CREATE TABLE IF NOT EXISTS menu(
  22. row INTEGER PRIMARY KEY AUTOINCREMENT
  23. );
  24. `
  25. const badNavbarItemsTable = `
  26. CREATE TABLE IF NOT EXISTS navbar(
  27. row INTEGER PRIMARY KEY AUTOINCREMENT
  28. );`
  29. const badAssetTable = `
  30. CREATE TABLE IF NOT EXISTS assets(
  31. row INTEGER PRIMARY KEY AUTOINCREMENT
  32. );
  33. `
  34. const badAdminTable = `
  35. CREATE TABLE IF NOT EXISTS admin(
  36. row INTEGER PRIMARY KEY AUTOINCREMENT
  37. );
  38. `
  39. var unpopulatedTables = []string{badPostsTable, badImagesTable, badNavbarItemsTable, badMenuItemsTable, badAssetTable, badAdminTable}
  40. /*
  41. creates in memory db and SQLiteRepo struct
  42. :param tmp: path to the temp directory for the filesystem IO struct to write images to
  43. :param migrate: choose to 'migrate' the database and create all the tables
  44. */
  45. func newTestDb(tmp string, migrate bool) (*SQLiteRepo, *sql.DB) {
  46. db, err := sql.Open("sqlite3", ":memory:")
  47. if err != nil {
  48. log.Fatal(err)
  49. }
  50. testDb := &SQLiteRepo{db: db, imageIO: FilesystemImageIO{RootDir: tmp}}
  51. if migrate {
  52. err = testDb.Migrate(RequiredTables)
  53. } else {
  54. err = testDb.Migrate(unpopulatedTables)
  55. }
  56. if err != nil {
  57. log.Fatal("failed to start the test database: ", err)
  58. }
  59. return testDb, db
  60. }
  61. func TestMigrate(t *testing.T) {
  62. requiredTables := []string{
  63. "posts",
  64. "images",
  65. "menu",
  66. "navbar",
  67. "assets",
  68. "admin",
  69. }
  70. db, err := sql.Open("sqlite3", ":memory:")
  71. if err != nil {
  72. log.Fatal(err)
  73. }
  74. testDb := &SQLiteRepo{db: db}
  75. err = testDb.Migrate(RequiredTables)
  76. if err != nil {
  77. t.Error(err)
  78. }
  79. for i := range requiredTables {
  80. name := requiredTables[i]
  81. row := db.QueryRow("SELECT name FROM sqlite_master WHERE type='table' AND name='?'", name)
  82. if row.Err() != nil {
  83. t.Errorf("error querying table: %s", name)
  84. }
  85. if row == nil {
  86. t.Errorf("no table returned: %s", name)
  87. }
  88. }
  89. }
  90. func TestGetDropdownElements(t *testing.T) {
  91. type testcase struct {
  92. seed []LinkPair
  93. }
  94. testDb, db := newTestDb(t.TempDir(), true)
  95. for _, tc := range []testcase{
  96. {
  97. seed: []LinkPair{
  98. {
  99. Text: "abc123",
  100. Link: "/abc/123",
  101. },
  102. },
  103. },
  104. } {
  105. stmt, _ := db.Prepare("INSERT INTO menu(link, text) VALUES (?,?)")
  106. for i := range tc.seed {
  107. _, err := stmt.Exec(tc.seed[i].Link, tc.seed[i].Text)
  108. if err != nil {
  109. t.Errorf("failed to seed: %s", err)
  110. }
  111. }
  112. got := testDb.GetDropdownElements()
  113. assert.Equal(t, got, tc.seed)
  114. }
  115. }
  116. func TestGetNavBarLinks(t *testing.T) {
  117. type testcase struct {
  118. seed []NavBarItem
  119. }
  120. testDb, db := newTestDb(t.TempDir(), true)
  121. for _, tc := range []testcase{
  122. {
  123. seed: []NavBarItem{
  124. {
  125. Link: "/abc/123",
  126. Redirect: "/abc/123/site",
  127. Png: []byte("xzy123abc098"),
  128. },
  129. },
  130. },
  131. } {
  132. stmt, _ := db.Prepare("INSERT INTO navbar(png, link, redirect) VALUES (?,?,?)")
  133. for i := range tc.seed {
  134. _, err := stmt.Exec(tc.seed[i].Png, tc.seed[i].Link, tc.seed[i].Redirect)
  135. if err != nil {
  136. t.Errorf("failed to seed: %s", err)
  137. }
  138. }
  139. got := testDb.GetNavBarLinks()
  140. assert.Equal(t, tc.seed, got)
  141. }
  142. }
  143. func TestGetAssets(t *testing.T) {
  144. type testcase struct {
  145. seed []Asset
  146. }
  147. testDb, db := newTestDb(t.TempDir(), true)
  148. for _, tc := range []testcase{
  149. {
  150. seed: []Asset{
  151. {
  152. Data: []byte("abc123xyz098"),
  153. Name: "asset1",
  154. },
  155. },
  156. },
  157. } {
  158. stmt, _ := db.Prepare("INSERT INTO assets(data, name) VALUES (?,?)")
  159. for i := range tc.seed {
  160. _, err := stmt.Exec(tc.seed[i].Data, tc.seed[i].Name)
  161. if err != nil {
  162. t.Error(err)
  163. }
  164. }
  165. got := testDb.GetAssets()
  166. assert.Equal(t, tc.seed, got)
  167. }
  168. }
  169. func TestGetAdminTables(t *testing.T) {
  170. type testcase struct {
  171. seed AdminPage
  172. }
  173. testDb, db := newTestDb(t.TempDir(), true)
  174. for _, tc := range []testcase{
  175. {
  176. seed: AdminPage{
  177. Tables: map[string][]TableData{
  178. "test": {
  179. {
  180. DisplayName: "abc123",
  181. Link: "xyz098",
  182. },
  183. },
  184. },
  185. },
  186. },
  187. } {
  188. stmt, _ := db.Prepare("INSERT INTO admin(display_name, link, category) VALUES (?,?,?)")
  189. for k, table := range tc.seed.Tables {
  190. for i := range table {
  191. _, err := stmt.Exec(table[i].DisplayName, table[i].Link, k)
  192. if err != nil {
  193. t.Error(err)
  194. }
  195. }
  196. }
  197. got := testDb.GetAdminTables()
  198. assert.Equal(t, tc.seed, got)
  199. }
  200. }
  201. func TestGetDocument(t *testing.T) {
  202. type testcase struct {
  203. seed Document
  204. }
  205. testDb, db := newTestDb(t.TempDir(), true)
  206. for _, tc := range []testcase{
  207. {
  208. seed: Document{
  209. Ident: Identifier("qwerty"),
  210. Title: "abc 123",
  211. Created: "2024-12-31",
  212. Body: "blog post body etc",
  213. Category: BLOG,
  214. Sample: "this is a sample",
  215. },
  216. },
  217. } {
  218. stmt, _ := db.Prepare("INSERT INTO posts(id, title, created, body, category, sample) VALUES (?,?,?,?,?,?)")
  219. _, err := stmt.Exec(tc.seed.Ident, tc.seed.Title, tc.seed.Created, tc.seed.Body, tc.seed.Category, tc.seed.Sample)
  220. if err != nil {
  221. t.Error(err)
  222. }
  223. got, _ := testDb.GetDocument(Identifier("qwerty"))
  224. assert.Equal(t, tc.seed, got)
  225. }
  226. }
  227. func TestGetByCategory(t *testing.T) {
  228. type testcase struct {
  229. seed []Document
  230. }
  231. testDb, db := newTestDb(t.TempDir(), true)
  232. for _, tc := range []testcase{
  233. {
  234. seed: []Document{
  235. {
  236. Row: 1,
  237. Ident: Identifier("qwerty"),
  238. Title: "abc 123",
  239. Created: "2024-12-31",
  240. Body: "blog post body etc",
  241. Category: BLOG,
  242. Sample: "this is a sample",
  243. },
  244. {
  245. Row: 2,
  246. Ident: Identifier("poiuyt"),
  247. Title: "abc 123",
  248. Created: "2024-12-31",
  249. Body: "blog post body etc",
  250. Category: BLOG,
  251. Sample: "this is a sample",
  252. },
  253. },
  254. },
  255. } {
  256. stmt, _ := db.Prepare("INSERT INTO posts(id, title, created, body, category, sample) VALUES (?,?,?,?,?,?)")
  257. for i := range tc.seed {
  258. _, err := stmt.Exec(tc.seed[i].Ident, tc.seed[i].Title, tc.seed[i].Created, tc.seed[i].Body, tc.seed[i].Category, tc.seed[i].Sample)
  259. if err != nil {
  260. t.Error(err)
  261. }
  262. }
  263. got := testDb.GetByCategory(BLOG)
  264. assert.Equal(t, tc.seed, got)
  265. }
  266. }
  267. func TestGetImage(t *testing.T) {
  268. testDb, db := newTestDb(t.TempDir(), true)
  269. type testcase struct {
  270. seed Image
  271. shouldSeed bool
  272. err error
  273. }
  274. for _, tc := range []testcase{
  275. {
  276. seed: Image{
  277. Ident: Identifier("abc123"),
  278. Title: "xyz098",
  279. Desc: "description",
  280. Created: "2024-12-31",
  281. Category: "homepage",
  282. Data: []byte("abc123xyz098"),
  283. },
  284. shouldSeed: true,
  285. err: nil,
  286. },
  287. {
  288. seed: Image{
  289. Ident: Identifier("zxcvbnm"),
  290. },
  291. shouldSeed: false,
  292. err: ErrNotExists,
  293. },
  294. } {
  295. if tc.shouldSeed {
  296. _, err := db.Exec("INSERT INTO images (id, title, desc, created, category) VALUES (?,?,?,?,?)", string(tc.seed.Ident), tc.seed.Title, tc.seed.Desc, "2024-12-31", tc.seed.Category)
  297. if err != nil {
  298. t.Error(err)
  299. }
  300. testDb.imageIO.Put(tc.seed.Data, tc.seed.Ident)
  301. }
  302. got, err := testDb.GetImage(tc.seed.Ident)
  303. if err != nil {
  304. assert.Equal(t, tc.err, err)
  305. } else {
  306. assert.Equal(t, tc.seed, got)
  307. }
  308. }
  309. }
  310. func TestGetAllImages(t *testing.T) {
  311. testDb, db := newTestDb(t.TempDir(), true)
  312. type testcase struct {
  313. seed []Image
  314. }
  315. for _, tc := range []testcase{
  316. {
  317. seed: []Image{
  318. {
  319. Ident: Identifier("abc123"),
  320. Title: "xyz098",
  321. Data: []byte("abc123xyz098"),
  322. Created: "2024-12-31",
  323. Desc: "description",
  324. Category: "homepage",
  325. },
  326. {
  327. Ident: Identifier("xyz098"),
  328. Title: "abc123",
  329. Data: []byte("abc123xyz098"),
  330. Created: "2024-12-31",
  331. Desc: "description",
  332. Category: "homepage",
  333. },
  334. },
  335. },
  336. } {
  337. for i := range tc.seed {
  338. _, err := db.Exec("INSERT INTO images (id, title, desc, created, category) VALUES (?,?,?,?,?)", string(tc.seed[i].Ident), tc.seed[i].Title, tc.seed[i].Desc, tc.seed[i].Created, tc.seed[i].Category)
  339. if err != nil {
  340. t.Error(err)
  341. }
  342. testDb.imageIO.Put(tc.seed[i].Data, tc.seed[i].Ident)
  343. }
  344. got := testDb.GetAllImages()
  345. for i := range tc.seed {
  346. tc.seed[i].Data = nil
  347. }
  348. assert.Equal(t, tc.seed, got)
  349. }
  350. }
  351. func TestAllDocuments(t *testing.T) {
  352. testDb, db := newTestDb(t.TempDir(), true)
  353. type testcase struct {
  354. seed []Document
  355. }
  356. for _, tc := range []testcase{
  357. {
  358. seed: []Document{
  359. {
  360. Row: 1,
  361. Ident: Identifier("qwerty"),
  362. Title: "abc 123",
  363. Created: "2024-12-31",
  364. Body: "blog post body etc",
  365. Category: BLOG,
  366. Sample: "this is a sample",
  367. },
  368. {
  369. Row: 2,
  370. Ident: Identifier("poiuyt"),
  371. Title: "abc 123",
  372. Created: "2024-12-31",
  373. Body: "blog post body etc",
  374. Category: BLOG,
  375. Sample: "this is a sample",
  376. },
  377. },
  378. },
  379. } {
  380. stmt, _ := db.Prepare("INSERT INTO posts(id, title, created, body, category, sample) VALUES (?,?,?,?,?,?)")
  381. for i := range tc.seed {
  382. _, err := stmt.Exec(tc.seed[i].Ident, tc.seed[i].Title, tc.seed[i].Created, tc.seed[i].Body, tc.seed[i].Category, tc.seed[i].Sample)
  383. if err != nil {
  384. t.Error(err)
  385. }
  386. }
  387. got := testDb.AllDocuments()
  388. assert.Equal(t, tc.seed, got)
  389. }
  390. }
  391. func TestUpdateDocument(t *testing.T) {
  392. type testcase struct {
  393. migrate bool
  394. seed Document
  395. input Document
  396. err error
  397. }
  398. for _, tc := range []testcase{
  399. {
  400. migrate: true,
  401. seed: Document{
  402. Row: 1,
  403. Ident: Identifier("qwerty"),
  404. Title: "abc 123",
  405. Created: "2024-12-31",
  406. Body: "blog post body etc",
  407. Category: BLOG,
  408. Sample: "this is a sample",
  409. },
  410. input: Document{
  411. Row: 1,
  412. Ident: Identifier("qwerty"),
  413. Title: "new title",
  414. Created: "2024-12-31",
  415. Body: "new updated post that must be reflected after the update",
  416. Category: BLOG,
  417. Sample: "new updated post that must be reflected after the update",
  418. },
  419. err: nil,
  420. },
  421. {
  422. migrate: true,
  423. seed: Document{
  424. Row: 1,
  425. Ident: Identifier("asdf"),
  426. Title: "abc 123",
  427. Created: "2024-12-31",
  428. Body: "blog post body etc",
  429. Category: BLOG,
  430. Sample: "this is a sample",
  431. },
  432. input: Document{
  433. Row: 1,
  434. Ident: Identifier("This id does not exist"),
  435. Title: "new title",
  436. Created: "2024-12-31",
  437. Body: "new updated post that must be reflected after the update",
  438. Category: BLOG,
  439. Sample: "new updated post that must be reflected after the update",
  440. },
  441. err: ErrNotExists,
  442. },
  443. {
  444. migrate: false, // not creating the database tables so we can error out the SQL statement execution
  445. seed: Document{
  446. Row: 1,
  447. Ident: Identifier("asdf"),
  448. Title: "abc 123",
  449. Created: "2024-12-31",
  450. Body: "blog post body etc",
  451. Category: BLOG,
  452. Sample: "this is a sample",
  453. },
  454. input: Document{
  455. Row: 1,
  456. Ident: Identifier("This id does not exist"),
  457. Title: "new title",
  458. Created: "2024-12-31",
  459. Body: "new updated post that must be reflected after the update",
  460. Category: BLOG,
  461. Sample: "new updated post that must be reflected after the update",
  462. },
  463. err: errors.New("no such column: title"),
  464. },
  465. } {
  466. testDb, db := newTestDb(t.TempDir(), tc.migrate)
  467. if tc.migrate {
  468. stmt, _ := db.Prepare("INSERT INTO posts(id, title, created, body, category, sample) VALUES (?,?,?,?,?,?)")
  469. _, err := stmt.Exec(tc.seed.Ident, tc.seed.Title, tc.seed.Created, tc.seed.Body, tc.seed.Category, tc.seed.Sample)
  470. if err != nil {
  471. t.Error(err)
  472. }
  473. }
  474. err := testDb.UpdateDocument(tc.input)
  475. if err != nil {
  476. assert.Equal(t, tc.err.Error(), err.Error())
  477. } else {
  478. row := db.QueryRow("SELECT * FROM posts WHERE id = ?", tc.seed.Ident)
  479. var got Document
  480. if err := row.Scan(&got.Row, &got.Ident, &got.Title, &got.Created, &got.Body, &got.Category, &got.Sample); err != nil {
  481. assert.Equal(t, tc.err, err)
  482. }
  483. assert.Equal(t, tc.input, got)
  484. }
  485. }
  486. }
  487. func TestAddImage(t *testing.T) {
  488. type testcase struct {
  489. data []byte
  490. title string
  491. desc string
  492. category string
  493. err error
  494. }
  495. testDb, _ := newTestDb(t.TempDir(), true)
  496. for _, tc := range []testcase{
  497. {
  498. data: []byte("abc123xyz098"),
  499. title: "dont matter",
  500. desc: "also dont matter",
  501. category: "homepage",
  502. },
  503. } {
  504. id, err := testDb.AddImage(tc.data, tc.title, tc.desc, tc.category, "image/jpeg")
  505. if err != nil {
  506. assert.Equal(t, tc.err, err)
  507. } else {
  508. b, err := testDb.imageIO.Get(id)
  509. if err != nil {
  510. t.Error(err)
  511. }
  512. assert.Equal(t, tc.data, b)
  513. }
  514. }
  515. }
  516. func TestAddMenuItem(t *testing.T) {
  517. type testcase struct {
  518. input []LinkPair
  519. err error
  520. }
  521. testDb, _ := newTestDb(t.TempDir(), true)
  522. for _, tc := range []testcase{
  523. {
  524. input: []LinkPair{
  525. {
  526. Text: "abc 123",
  527. Link: "/abc/123",
  528. },
  529. },
  530. err: nil,
  531. },
  532. } {
  533. for i := range tc.input {
  534. err := testDb.AddMenuItem(tc.input[i])
  535. if err != nil {
  536. // assert.Equal(expected, actual)
  537. assert.Equal(t, tc.err, err)
  538. }
  539. rows, err := testDb.db.Query("SELECT * FROM menu")
  540. var got []LinkPair
  541. defer rows.Close()
  542. for rows.Next() {
  543. var item LinkPair
  544. var id int
  545. err = rows.Scan(&id, &item.Link, &item.Text)
  546. if err != nil {
  547. t.Errorf("failed: %s", err.Error())
  548. }
  549. got = append(got, item)
  550. }
  551. assert.Equal(t, tc.input, got)
  552. }
  553. }
  554. }
  555. func TestAddNavbarItem(t *testing.T) {
  556. type testcase struct {
  557. input []NavBarItem
  558. err error
  559. }
  560. testDb, _ := newTestDb(t.TempDir(), true)
  561. for _, tc := range []testcase{
  562. {
  563. input: []NavBarItem{
  564. {
  565. Redirect: "http://whatever.com",
  566. Link: "api/stuff/picture.jpeg",
  567. Png: []byte(""),
  568. },
  569. },
  570. },
  571. } {
  572. for i := range tc.input {
  573. err := testDb.AddNavbarItem(tc.input[i])
  574. if err != nil {
  575. assert.Equal(t, tc.err, err)
  576. }
  577. }
  578. rows, err := testDb.db.Query("SELECT * FROM navbar")
  579. var got []NavBarItem
  580. defer rows.Close()
  581. for rows.Next() {
  582. var item NavBarItem
  583. var id int
  584. err = rows.Scan(&id, &item.Png, &item.Link, &item.Redirect)
  585. if err != nil {
  586. log.Fatal(err)
  587. }
  588. got = append(got, item)
  589. }
  590. assert.Equal(t, tc.input, got)
  591. }
  592. }
  593. func TestAddAsset(t *testing.T) {
  594. type testcase struct {
  595. input []Asset
  596. err error
  597. }
  598. testDb, _ := newTestDb(t.TempDir(), true)
  599. for _, tc := range []testcase{
  600. {
  601. input: []Asset{
  602. {
  603. Data: []byte(""),
  604. Name: "",
  605. },
  606. },
  607. },
  608. } {
  609. for i := range tc.input {
  610. err := testDb.AddAsset(tc.input[i].Name, tc.input[i].Data)
  611. if err != nil {
  612. assert.Equal(t, tc.err, err)
  613. }
  614. }
  615. rows, err := testDb.db.Query("SELECT * FROM assets")
  616. var assets []Asset
  617. defer rows.Close()
  618. for rows.Next() {
  619. var item Asset
  620. var id int
  621. err = rows.Scan(&id, &item.Name, &item.Data)
  622. if err != nil {
  623. log.Fatal(err)
  624. }
  625. assets = append(assets, item)
  626. }
  627. }
  628. }
  629. func TestAddDocument(t *testing.T) {
  630. testDb, db := newTestDb(t.TempDir(), true)
  631. type testcase struct {
  632. seed Document
  633. err error
  634. }
  635. for _, tc := range []testcase{
  636. {
  637. seed: Document{
  638. Title: "abc 123",
  639. Body: "blog post body etc",
  640. Created: "2024-12-31",
  641. Category: BLOG,
  642. Sample: "this is a sample",
  643. },
  644. err: nil,
  645. },
  646. } {
  647. id, err := testDb.AddDocument(tc.seed)
  648. if err != nil {
  649. assert.Equal(t, tc.err, err)
  650. }
  651. row := db.QueryRow("SELECT * FROM posts WHERE id = ?", id)
  652. var got Document
  653. var rowNum int
  654. if err := row.Scan(&rowNum, &got.Ident, &got.Title, &got.Created, &got.Body, &got.Category, &got.Sample); err != nil {
  655. assert.Equal(t, tc.err, err)
  656. }
  657. want := Document{
  658. Ident: id,
  659. Title: tc.seed.Title,
  660. Body: tc.seed.Body,
  661. Category: tc.seed.Category,
  662. Created: tc.seed.Created,
  663. Sample: tc.seed.MakeSample(),
  664. }
  665. assert.Equal(t, want, got)
  666. }
  667. }
  668. func TestAddAdminTableEntry(t *testing.T) {
  669. type testcase struct {
  670. input AdminPage
  671. err error
  672. }
  673. testDb, _ := newTestDb(t.TempDir(), true)
  674. for _, tc := range []testcase{
  675. {
  676. input: AdminPage{
  677. Tables: map[string][]TableData{
  678. "test category": {
  679. {
  680. DisplayName: "abc 123",
  681. Link: "/abc/123",
  682. },
  683. },
  684. },
  685. },
  686. err: nil,
  687. },
  688. } {
  689. for ctg, tables := range tc.input.Tables {
  690. for i := range tables {
  691. err := testDb.AddAdminTableEntry(tables[i], ctg)
  692. if err != nil {
  693. assert.Equal(t, tc.err, err)
  694. }
  695. }
  696. }
  697. rows, err := testDb.db.Query("SELECT * FROM admin")
  698. got := AdminPage{Tables: map[string][]TableData{}}
  699. defer rows.Close()
  700. for rows.Next() {
  701. var item TableData
  702. var id int
  703. var category string
  704. err = rows.Scan(&id, &item.DisplayName, &item.Link, &category)
  705. if err != nil {
  706. log.Fatal(err)
  707. }
  708. got.Tables[category] = append(got.Tables[category], item)
  709. }
  710. assert.Equal(t, tc.input, got)
  711. }
  712. }
  713. func TestDeleteDocument(t *testing.T) {
  714. type testcase struct {
  715. input Document
  716. err error
  717. }
  718. testDb, db := newTestDb(t.TempDir(), true)
  719. for _, tc := range []testcase{
  720. {
  721. input: Document{
  722. Title: "abc 123",
  723. Body: "blog post body etc",
  724. Created: "2024-12-31",
  725. Category: BLOG,
  726. Sample: "this is a sample",
  727. },
  728. err: nil,
  729. },
  730. } {
  731. id, err := testDb.AddDocument(tc.input)
  732. if err != nil {
  733. t.Error("failed to add doc: ", err)
  734. }
  735. err = testDb.DeleteDocument(id)
  736. if err != nil {
  737. assert.Equal(t, tc.err, err)
  738. }
  739. row, _ := db.Query("SELECT * FROM posts")
  740. if row.Next() {
  741. t.Error("Too many rows returned after deleting")
  742. }
  743. }
  744. }
  745. func TestGetImageStore(t *testing.T) {
  746. // testDb, db := newTestDb(t.TempDir(), true)
  747. }
  748. func TestNewIdentifier(t *testing.T) {
  749. // testDb, db := newTestDb(t.TempDir(), true)
  750. }
  751. func TestImageMediaTypeRoundTrip(t *testing.T) {
  752. repo, db := newTestDb(t.TempDir(), true)
  753. defer db.Close()
  754. id, err := repo.AddImage([]byte("image bytes"), "title", "description", DIGITAL_ART, "image/webp")
  755. if err != nil {
  756. t.Fatal(err)
  757. }
  758. image, err := repo.GetImage(id)
  759. if err != nil {
  760. t.Fatal(err)
  761. }
  762. assert.Equal(t, "image/webp", image.MediaType)
  763. assert.Equal(t, []byte("image bytes"), image.Data)
  764. image.Data = nil
  765. assert.Equal(t, []Image{image}, repo.GetAllImages())
  766. assert.Equal(t, []Image{image}, repo.GetImagesByCategory(DIGITAL_ART))
  767. assert.Empty(t, repo.GetImagesByCategory(MISC_ASSET))
  768. }
  769. func TestLegacyImagesMigration(t *testing.T) {
  770. db, err := sql.Open("sqlite3", ":memory:")
  771. if err != nil {
  772. t.Fatal(err)
  773. }
  774. defer db.Close()
  775. repo := NewSQLiteRepo(db, FilesystemImageIO{RootDir: t.TempDir()})
  776. // An extra column and a different physical order simulate an older database.
  777. _, err = db.Exec(`CREATE TABLE images (
  778. row INTEGER PRIMARY KEY AUTOINCREMENT,
  779. id TEXT NOT NULL, legacy_extra TEXT, category TEXT NOT NULL,
  780. title TEXT NOT NULL, desc TEXT NOT NULL, created TEXT NOT NULL
  781. )`)
  782. if err != nil {
  783. t.Fatal(err)
  784. }
  785. _, err = db.Exec(`INSERT INTO images (id, legacy_extra, category, title, desc, created)
  786. VALUES ('old', 'unused', 'digital_art', 'old title', 'old desc', 'yesterday')`)
  787. if err != nil {
  788. t.Fatal(err)
  789. }
  790. if err := repo.imageIO.Put([]byte("old bytes"), "old"); err != nil {
  791. t.Fatal(err)
  792. }
  793. for i := 0; i < 2; i++ {
  794. if err := repo.Migrate(RequiredTables); err != nil {
  795. t.Fatal(err)
  796. }
  797. }
  798. old, err := repo.GetImage("old")
  799. if err != nil {
  800. t.Fatal(err)
  801. }
  802. assert.Equal(t, Image{Ident: "old", Title: "old title", Desc: "old desc", Created: "yesterday", Category: DIGITAL_ART, Data: []byte("old bytes")}, old)
  803. id, err := repo.AddImage([]byte("new bytes"), "new title", "new desc", DIGITAL_ART, "image/png")
  804. if err != nil {
  805. t.Fatal(err)
  806. }
  807. newImage, err := repo.GetImage(id)
  808. if err != nil {
  809. t.Fatal(err)
  810. }
  811. assert.Equal(t, "image/png", newImage.MediaType)
  812. old.Data = nil
  813. newImage.Data = nil
  814. assert.Equal(t, []Image{old, newImage}, repo.GetAllImages())
  815. assert.Equal(t, []Image{old, newImage}, repo.GetImagesByCategory(DIGITAL_ART))
  816. }