aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2019-09-29 14:59:00 +0000
committers-ol <s-ol@users.noreply.github.com>2019-09-29 14:59:00 +0000
commit7cb951d05eb9f2408ee34f7c5f0b49907a03bbfc (patch)
tree67a077382fd8984ff216824f713df0c38c5dc5a1
parentinitial sqlite support (diff)
downloadmmm-7cb951d05eb9f2408ee34f7c5f0b49907a03bbfc.tar.gz
mmm-7cb951d05eb9f2408ee34f7c5f0b49907a03bbfc.zip
add busted spec for driver
-rw-r--r--mmm/mmmfs/drivers/sql.moon72
-rw-r--r--spec/driver_spec.moon88
-rw-r--r--tests/test_backend.moon49
3 files changed, 138 insertions, 71 deletions
diff --git a/mmm/mmmfs/drivers/sql.moon b/mmm/mmmfs/drivers/sql.moon
index cfe60e6..a961e1e 100644
--- a/mmm/mmmfs/drivers/sql.moon
+++ b/mmm/mmmfs/drivers/sql.moon
@@ -2,24 +2,27 @@ sqlite = require 'sqlite3'
class TreeStore
new: (name='db.sqlite3') =>
- @db = sqlite.open name
+ @db = if name then sqlite.open name else sqlite.open_memory!
assert @db\exec [[
PRAGMA foreign_keys = ON;
+ PRAGMA case_sensitive_like = ON;
CREATE TABLE IF NOT EXISTS fileder (
id INTEGER NOT NULL PRIMARY KEY,
path TEXT NOT NULL UNIQUE,
- parent TEXT
+ parent TEXT REFERENCES fileder(path)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE
);
CREATE TABLE IF NOT EXISTS facet (
fileder_id INTEGER NOT NULL
REFERENCES fileder
ON UPDATE CASCADE
ON DELETE CASCADE,
- name TEXT,
- type TEXT,
- value BLOB,
- PRIMARY KEY (name, type)
+ name TEXT NOT NULL,
+ type TEXT NOT NULL,
+ value BLOB NOT NULL,
+ PRIMARY KEY (fileder_id, name, type)
);
CREATE INDEX IF NOT EXISTS facet_fileder_id ON facet(fileder_id);
CREATE INDEX IF NOT EXISTS facet_name ON facet(name);
@@ -30,47 +33,60 @@ class TreeStore
fetch: (q, ...) =>
stmt = assert @db\prepare q
- stmt\bind ...
+ stmt\bind ... if 0 < select '#', ...
stmt\irows!
fetch_one: (q, ...) =>
stmt = assert @db\prepare q
- stmt\bind ...
+ stmt\bind ... if 0 < select '#', ...
stmt\first_irow!
exec: (q, ...) =>
stmt = assert @db\prepare q
- stmt\bind ...
- assert stmt\exec!
+ stmt\bind ... if 0 < select '#', ...
+ res = assert stmt\exec!
-- fileders
- list_fileders: (path, recursive=false) =>
+ list_fileders_in: (path) =>
coroutine.wrap ->
for { path } in @fetch 'SELECT path
- FROM fileder WHERE parent = ?', path
+ FROM fileder WHERE parent IS ?', path
coroutine.yield path
- if recursive
- for p in @list_fileders path
- coroutine.yield p
+
+ list_all_fileders: (path) =>
+ coroutine.wrap ->
+ for path in @list_fileders_in path
+ coroutine.yield path
+ for p in @list_all_fileders path
+ coroutine.yield p
create_fileder: (parent, name) =>
@exec 'INSERT INTO fileder (path, parent)
- VALUES (:path, :parent)',
- { path: "#{parent}/#{name}", :parent }
+ VALUES (IFNULL(:parent, "") || "/" || :name, :parent)',
+ { :parent, :name }
+
+ changes = @fetch_one 'SELECT changes()'
+ assert changes[1] == 1, "couldn't create fileder - parent missing?"
remove_fileder: (path) =>
@exec 'DELETE FROM fileder
- WHERE path = ?', path
+ WHERE path LIKE :path || "/%"
+ OR path = :path', path
rename_fileder: (path, next_name) =>
+ error 'not implemented'
+
@exec 'UPDATE fileder
- SET path = CONCAT(parent, "/", :next_name)
+ SET path = IFNULL(parent, "") || "/" || :next_name
WHERE path = :path',
{ :path, :next_name }
+
-- @TODO: rename all children, child-children...
- move_fileder: (path, new_path) =>
- error '@TODO: implement move_fileder'
+ move_fileder: (path, new_parent) =>
+ error 'not implemented'
+
+ -- @TODO: remove all children, child-children...
-- facets
list_facets: (path) =>
@@ -89,7 +105,7 @@ class TreeStore
AND facet.name = :name
AND facet.type = :type',
{ :path, :name, :type }
- v[1]
+ v and v[1]
create_facet: (path, name, type, blob) =>
@exec 'INSERT INTO facet (fileder_id, name, type, value)
@@ -98,6 +114,9 @@ class TreeStore
WHERE fileder.path = :path',
{ :path, :name, :type, :blob }
+ changes = @fetch_one 'SELECT changes()'
+ assert changes[1] == 1, "couldn't create facet - fileder missing?"
+
remove_facet: (path, name, type) =>
@exec 'DELETE FROM facet
WHERE name = :name
@@ -105,6 +124,9 @@ class TreeStore
AND fileder_id = (SELECT id FROM fileder WHERE path = :path)',
{ :path, :name, :type }
+ changes = @fetch_one 'SELECT changes()'
+ assert changes[1] == 1, "no such facet"
+
rename_facet: (path, name, type, next_name) =>
@exec 'UPDATE facet
SET name = :next_name
@@ -113,6 +135,9 @@ class TreeStore
AND fileder_id = (SELECT id FROM fileder WHERE path = :path)',
{ :path, :name, :next_name, :type }
+ changes = @fetch_one 'SELECT changes()'
+ assert changes[1] == 1, "no such facet"
+
update_facet: (path, name, type, blob) =>
@exec 'UPDATE facet
SET value = :blob
@@ -121,6 +146,9 @@ class TreeStore
AND facet.fileder_id = (SELECT id FROM fileder WHERE path = :path)',
{ :path, :name, :type, :blob }
+ changes = @fetch_one 'SELECT changes()'
+ assert changes[1] == 1, "no such facet"
+
load_tree = (file='root.zip') ->
archive = zip.open file
diff --git a/spec/driver_spec.moon b/spec/driver_spec.moon
new file mode 100644
index 0000000..60cd574
--- /dev/null
+++ b/spec/driver_spec.moon
@@ -0,0 +1,88 @@
+import TreeStore from require 'mmm.mmmfs.drivers.sql'
+
+sort2 = (a, b) ->
+ {ax, ay}, {bx, by} = a, b
+ "#{ax}//#{ay}" < "#{bx}//#{by}"
+toseq = (iter) -> with v = [x for x in iter]
+ table.sort v
+toseq2 = (iter) -> with v = [{x, y} for x, y in iter]
+ table.sort v, sort2
+
+describe "sql driver", ->
+ randomize false
+
+ ts = TreeStore!
+
+ it "starts out empty", ->
+ assert.are.same {}, toseq ts\list_fileders_in!
+ assert.are.same {}, toseq ts\list_all_fileders!
+
+ it "can't create fileders without missing parents", ->
+ assert.has_error ->
+ ts\create_fileder '/hello', 'world'
+
+ it "can create root fileders", ->
+ ts\create_fileder nil, 'hello'
+ assert.are.same {'/hello'}, toseq ts\list_all_fileders!
+
+ it "can create and list child fileders recursively", ->
+ ts\create_fileder '/hello', 'world'
+ ts\create_fileder '/hello/world', 'again'
+
+ assert.are.same {'/hello', '/hello/world', '/hello/world/again'},
+ toseq ts\list_all_fileders!
+
+ it "can list immediate children", ->
+ assert.are.same {"/hello"}, toseq ts\list_fileders_in!
+ assert.are.same {"/hello/world"}, toseq ts\list_fileders_in "/hello"
+ assert.are.same {"/hello/world/again"}, toseq ts\list_fileders_in "/hello/world"
+
+ describe "can create and list facets", ->
+ ts\create_facet '/hello', 'name', 'alpha', 'hello'
+ ts\create_facet '/hello/world', 'name', 'alpha', 'world'
+ ts\create_facet '/hello/world', '', 'text/markdown', '# Helau World!'
+
+ it "can't create facet for nonexistant fileders", ->
+ assert.has_error -> ts\create_facet '/hello/orldw', 'name', 'alpha', 'foo'
+
+ it "can't create facet without value", ->
+ assert.has_error -> ts\create_facet '/hello/world', 'other', 'alpha', nil
+
+ it "can't create facet for duplicate keys", ->
+ assert.has_error -> ts\create_facet '/hello/world', 'name', 'alpha', 'foo'
+
+ assert.are.same {{'name', 'alpha'}}, toseq2 ts\list_facets '/hello'
+ assert.are.same {{'', 'text/markdown'}, {'name', 'alpha'}},
+ toseq2 ts\list_facets '/hello/world'
+
+ it "can load facets", ->
+ assert.are.equal 'hello', ts\load_facet '/hello', 'name', 'alpha'
+ assert.are.equal 'world', ts\load_facet '/hello/world', 'name', 'alpha'
+ assert.are.equal '# Helau World!', ts\load_facet '/hello/world', '', 'text/markdown'
+ assert.is_falsy ts\load_facet '/hello', 'nonexistant', 'facet'
+
+ it "can rename facets", ->
+ ts\rename_facet '/hello/world', 'name', 'alpha', 'gnome'
+ assert.are.same {{'', 'text/markdown'}, {'gnome', 'alpha'}},
+ toseq2 ts\list_facets '/hello/world'
+ assert.are.equal 'world', ts\load_facet '/hello/world', 'gnome', 'alpha'
+
+ it "can update facets", ->
+ ts\update_facet '/hello/world', '', 'text/markdown', '# Hello World!'
+ assert.are.same {{'', 'text/markdown'}, {'gnome', 'alpha'}},
+ toseq2 ts\list_facets '/hello/world'
+ assert.are.equal '# Hello World!', ts\load_facet '/hello/world', '', 'text/markdown'
+
+ it "can remove facets", ->
+ ts\remove_facet '/hello/world', 'gnome', 'alpha'
+ assert.are.same {{'', 'text/markdown'}}, toseq2 ts\list_facets '/hello/world'
+
+ assert.has_error -> ts\remove_facet '/hello/world', 'gnome', 'alpha'
+
+ it "can delete fileders", ->
+ ts\remove_fileder '/hello/world'
+ assert.is_falsy ts\load_facet '/hello/world', 'gnome', 'alpha'
+ assert.are.same {'/hello'}, toseq ts\list_all_fileders!
+
+ ts\remove_fileder '/hello'
+ assert.are.same {}, toseq ts\list_all_fileders!
diff --git a/tests/test_backend.moon b/tests/test_backend.moon
deleted file mode 100644
index d217c47..0000000
--- a/tests/test_backend.moon
+++ /dev/null
@@ -1,49 +0,0 @@
-import TreeStore from require 'mmm.mmmfs.drivers.sql'
-ts = TreeStore!
-
-assert_seq1 = (expected, iter) ->
- tbl
- i = 0
-
- for x in iter
- i += 1
- assert expected[i] == x, "entry #{i}: '#{x}', expected '#{expected[i]}'!"
-
- assert i == #expected, "only #{i} entries found, expected #{#expected}"
-
-assert_seq2 = (expected, iter) ->
- tbl
- i = 0
-
- for a, b in iter
- i += 1
- assert expected[i][1] == a, "entry #{i} a: '#{a}', expected '#{expected[i][1]}'!"
- assert expected[i][2] == b, "entry #{i} b: '#{b}', expected '#{expected[i][2]}'!"
-
- assert i == #expected, "only #{i} entries found, expected #{#expected}"
-
-assert_seq1 {}, ts\list_fileders ''
-assert_seq1 {}, ts\list_fileders '', true
-
-ts\create_fileder '', 'hello'
-ts\create_fileder '/hello', 'world'
-assert_seq1 {'/hello', '/hello/world'}, ts\list_fileders '', true
-
-ts\create_facet '/hello/world', '', 'text/markdown', '# Helau World!'
-ts\create_facet '/hello/world', 'nome', 'alpha', 'world'
-
-assert_seq2 {}, ts\list_facets '/hello'
-assert_seq2 { {'', 'text/markdown'}, {'nome', 'alpha'} }, ts\list_facets '/hello/world'
-
-ts\rename_facet '/hello/world', 'nome', 'alpha', 'name'
-assert_seq2 { {'', 'text/markdown'}, {'name', 'alpha'} }, ts\list_facets '/hello/world'
-
-assert ('# Helau World!' == ts\load_facet '/hello/world', '', 'text/markdown')
-ts\update_facet '/hello/world', '', 'text/markdown', '# Hello World!'
-assert ('# Hello World!' == ts\load_facet '/hello/world', '', 'text/markdown')
-
-ts\remove_fileder '/hello/world'
-ts\remove_fileder '/hello'
-
-assert_seq2 {}, ts\list_facets '/hello/world'
-assert_seq1 {}, ts\list_fileders '', true