UNPKG

subsystems

Version:
480 lines (399 loc) 15.1 kB
chai = require 'chai' expect = chai.expect util = require 'util' s = require 'p/subsystems' OK = null #for err fields mk_base = (err, init, fmap) -> (val, map) -> ss = {} init ss, val for k, v of map ss[k] = s.inject v ss.start = (cont) -> cont err, fmap ss ss mk = mk_base OK, (o, val) -> o.val = val (o) -> o #erroneous subsystem mk_err = (err, map) -> (mk_base err, (o, val) -> 0 (o) -> o )(undefined, map) catching = (done, fn) -> try return done v if (v=fn()) instanceof Error catch err then return done(err) done OK #done: mocha.done #extra: tests(api) expect_ok = (done, extra) -> extra = extra or ->OK #return callback to .start (err, api) -> catching done, -> if err then throw err extra api #done: mocha.done #extra: tests(err) expect_err = (done, extra) -> extra = extra or ->OK #return callback to .start (err, api) -> catching done, -> unless err then throw new Error "Missing error (api: #{JSON.stringify api})" extra err #variant: expect_ok | expect_err #sysmap: passed to s.system #extra: passed to variant system_test = (variant, sysmap, extra) -> (done) -> s.start (s.system sysmap), variant done, extra describe 'trivial', -> it 'has all methods', -> expect(s.system).to.exist expect(s.inject).to.exist expect(s.start).to.exist expect(s.field).to.exist expect(s.fmap).to.exist expect(s.wrap).to.exist expect(s.rename).to.exist expect(s.subsystem).to.exist expect(s.subsystem.async).to.exist expect(s.subsystem.ret).to.exist expect(s.subsystem.ret.async).to.exist it 'creates system', -> sys = s.system {} expect(sys.start).to.be.a 'function' it 'creates subsystem', -> sys = do s.subsystem {}, -> do=> #takes different context @a = 5 expect(sys.start).to.be.a 'function' describe 'system', -> describe 'working', -> describe 'simple', -> it 'empty', system_test expect_ok, {}, (api) -> expect(api).to.eql {} it 'a', system_test expect_ok, a: mk 42, {} (api) -> expect(api).to.have.deep.property 'a.val', 42 it 'a,b', system_test expect_ok, a: mk 42, {} b: mk 12, {} (api) -> expect(api).to.have.deep.property 'a.val', 42 expect(api).to.have.deep.property 'b.val', 12 it 'a<b', system_test expect_ok, a: mk 42, {} b: mk 12, {da:'a'} (api) -> expect(api).to.have.deep.property 'a.val', 42 expect(api).to.have.deep.property 'b.val', 12 expect(api).to.have.deep.property 'b.da', api.a it 'a<b<c', system_test expect_ok, a: mk 42, {} b: mk 12, {da:'a'} c: mk 28, {db:'b'} (api) -> expect(api).to.have.deep.property 'a.val', 42 expect(api).to.have.deep.property 'b.val', 12 expect(api).to.have.deep.property 'c.val', 28 expect(api).to.have.deep.property 'b.da', api.a expect(api).to.have.deep.property 'c.db', api.b it '(a<b)<c', system_test expect_ok, a: mk 42, {} b: mk 12, {da:'a'} c: mk 28, {da:'a', db:'b'} (api) -> expect(api).to.have.deep.property 'a.val', 42 expect(api).to.have.deep.property 'b.val', 12 expect(api).to.have.deep.property 'c.val', 28 expect(api).to.have.deep.property 'b.da', api.a expect(api).to.have.deep.property 'c.da', api.a expect(api).to.have.deep.property 'c.db', api.b expect(api).to.have.deep.property 'c.db.da', api.a describe 'subsystems', -> it 'a.b', system_test expect_ok, a: s.system b: mk 42, {} (api) -> expect(api).to.have.deep.property 'a.b.val', 42 it 'a.b, a.kc', system_test expect_ok, c: mk 12, {} a: s.system kc: s.inject 'c' b: mk 42, {ka:'kc'} (api) -> expect(api).to.have.deep.property 'a.b.val', 42 expect(api).to.have.deep.property 'a.kc.val', 12 expect(api).to.have.deep.property 'a.b.ka.val', 12 it 'a,b', system_test expect_ok, a: s.system r: mk 10, {} aq: mk 42, {dr:'r'} b: s.system bq: mk 12, {} (api) -> expect(api).to.have.deep.property 'a.aq.val', 42 expect(api).to.have.deep.property 'a.r.val', 10 expect(api).to.have.deep.property 'b.bq.val', 12 expect(api).to.have.deep.property 'a.aq.dr.val', 10 expect(api).to.have.deep.property 'a.aq.dr', api.a.r it 'a<b', system_test expect_ok, a: s.system r: mk 10, {} aq: mk 42, {dr:'r'} b: s.system z: s.inject 'a' bq: mk 12, {dz:'z'} (api) -> expect(api).to.have.deep.property 'a.aq.val', 42 expect(api).to.have.deep.property 'a.r.val', 10 expect(api).to.have.deep.property 'b.bq.val', 12 expect(api).to.have.deep.property 'a.aq.dr.val', 10 expect(api).to.have.deep.property 'a.aq.dr', api.a.r expect(api).to.have.deep.property 'b.z', api.a expect(api).to.have.deep.property 'b.bq.dz.aq.dr.val', 10 describe 'base subsystem', -> it 'simple', system_test expect_ok, a: do s.subsystem {}, ->do=> @b=42 (api) -> expect(api).to.have.deep.property 'a.b', 42 it 'a<s<k', system_test expect_ok, a: mk 1 s: do s.subsystem da:'a' ->do=> @start= (42 + @da.val) #can export start k: s.field 's', 'start' (api) -> expect(api).to.have.deep.property 's.start', (42+api.a.val) expect(api).to.have.deep.property 'k', (42+api.a.val) expect(api).to.not.have.deep.property 's.da' it 'a<s<k async', system_test expect_ok, a: mk 1 s: do s.subsystem.async da:'a' (done)->do=> @start= (42 + @da.val) setTimeout done, 2 k: s.field 's', 'start' (api) -> expect(api).to.have.deep.property 's.start', (42+api.a.val) expect(api).to.have.deep.property 'k', (42+api.a.val) expect(api).to.not.have.deep.property 's.da' it 'a<s<k ret', system_test expect_ok, a: mk 1 s: do s.subsystem.ret da:'a' ->do=> {start: (42 + @da.val)} k: s.field 's', 'start' (api) -> expect(api).to.have.deep.property 's.start', (42+api.a.val) expect(api).to.have.deep.property 'k', (42+api.a.val) expect(api).to.not.have.deep.property 's.da' it 'a<s<k ret.async', system_test expect_ok, a: mk 1 s: do s.subsystem.ret.async da:'a' (cont)->do=> setTimeout (=>cont OK, {start: (42 + @da.val)}), 2 k: s.field 's', 'start' (api) -> expect(api).to.have.deep.property 's.start', (42+api.a.val) expect(api).to.have.deep.property 'k', (42+api.a.val) expect(api).to.not.have.deep.property 's.da' describe 'fmap,field,rename', -> it 'rename', system_test expect_ok, a: mk 42, {} b: s.rename (mk 12, {q:'test'}), test:'a' (api) -> expect(api).to.have.deep.property 'b.val', 12 expect(api).to.have.deep.property 'b.q.val', 42 it 'rename - partial', system_test expect_ok, a: mk 42, {} b: s.rename (mk 12, {q:'test', c:'c'}), test:'a' c: mk 1, {} (api) -> expect(api).to.have.deep.property 'b.val', 12 expect(api).to.have.deep.property 'c.val', 1 expect(api).to.have.deep.property 'b.q.val', 42 expect(api).to.have.deep.property 'b.c.val', 1 it 'field', system_test expect_ok, a: mk 42, {} b: s.field 'a', 'val' # on dep 'a' access field 'val' (api) -> expect(api).to.have.deep.property 'a.val', 42 expect(api).to.have.deep.property 'b', 42 it 'field - multi', system_test expect_ok, a: mk {a:b:42}, {} b: s.field 'a', 'val', 'a', 'b' (api) -> expect(api).to.have.deep.property 'a.val.a.b', 42 expect(api).to.have.deep.property 'b', 42 it 'fmap - replace', system_test expect_ok, a: mk 42, {} b: s.fmap 'a', (a) -> if a.val is 42 then 1337 else 0 (api) -> expect(api).to.have.deep.property 'a.val', 42 expect(api).to.have.deep.property 'b', 1337 it 'field rename', system_test expect_ok, a: mk 42, {} b: s.rename (s.field 'test', 'val'), test:'a' (api) -> expect(api).to.have.deep.property 'a.val', 42 expect(api).to.have.deep.property 'b', 42 it 'subsystem field rename', system_test expect_ok, a: s.system g: mk 42, {} b: s.rename (s.field 'test', 'g'), test:'a' v: s.field 'b', 'val' # also field of field ^^ (api) -> expect(api).to.have.deep.property 'a.g.val', 42 expect(api).to.have.deep.property 'b.val', 42 expect(api).to.have.deep.property 'v', 42 it 'wrap', system_test expect_ok, a: s.wrap 42 (api) -> expect(api).to.have.property 'a', 42 it 'wrap<b, fmap', system_test expect_ok, a: s.wrap 42 b: mk 8, {da:'a'} c: s.fmap 'a', (a) -> a + 8 (api) -> expect(api).to.have.deep.property 'a', 42 expect(api).to.have.deep.property 'b.val', 8 expect(api).to.have.deep.property 'b.da', 42 expect(api).to.have.deep.property 'c', 50 describe 'broken', -> describe 'unmet dependencies', -> it 'a', system_test expect_err, a: mk 42, {dx:'nonexistent_dep'} (err) -> expect(err).to.have.property 'message' .and.to.contain 'nmet dependenc' #[Uu]nmet c(ies|y) it 'a,b', system_test expect_err, a: mk 42, {dx:'nonexistent_dep'} b: mk 40, {dx:'nonexistent_dep'} (err) -> expect(err).to.have.property 'message' .and.to.contain 'nmet dependenc' #[Uu]nmet c(ies|y) it 'a>b', system_test expect_err, a: mk 42, {dx:'b'} b: mk 40, {dx:'nonexistent_dep'} (err) -> expect(err).to.have.property 'message' .and.to.contain 'nmet dependenc' #[Uu]nmet c(ies|y) describe 'cyclic dependencies', -> it 'a<>b', system_test expect_err, a: mk 42, {db:'b'} b: mk 40, {da:'a'} (err) -> expect(err).to.have.property 'message' .and.to.contain 'yclic dependenc' #[Cc]... it 'c<a<b<c', system_test expect_err, a: mk 42, {db:'c'} b: mk 40, {da:'a'} c: mk 44, {da:'b'} (err) -> expect(err).to.have.property 'message' .and.to.contain 'yclic dependenc' #[Cc]... it 'c<a<b<c, d', system_test expect_err, a: mk 42, {db:'c'} b: mk 40, {da:'a'} c: mk 44, {da:'b'} d: mk 13, {dx:'a'} (err) -> expect(err).to.have.property 'message' .and.to.contain 'yclic dependenc' #[Cc]... .and.to.not.contain '"d"' #d isn't part of cycle it '?<a<>b', system_test expect_err, # cyclic has priority over missing a: mk 42, {db:'b', dx:'nonexistent_dep'} b: mk 40, {da:'a', dx:'nonexistent_dep2'} (err) -> expect(err).to.have.property 'message' .and.to.contain 'yclic dependenc' #[Cc]... it 'a<>a', system_test expect_err, a: mk 42, {db:'a'} (err) -> expect(err).to.have.property 'message' .and.to.contain 'yclic dependenc' #[Cc]... it 'a<>(b->a)', system_test expect_err, a: s.rename (mk 42, {db:'b'}), b:'a' (err) -> expect(err).to.have.property 'message' .and.to.contain 'yclic dependenc' #[Cc]... it 'between subsystems', system_test expect_err, a: s.system ax: s.inject 'x' ay: mk 42, {dax:'ax'} b: s.system bx: mk 12, {dby:'by'} by: s.inject 'y' x: s.field 'b', 'bx' y: s.field 'a', 'ay' (err) -> expect(err).to.have.property 'message' .and.to.contain 'yclic dependenc' #[Cc]... describe 'error while starting subsystem', -> it 'a', do(er=new Error)-> system_test expect_err, a: mk_err er, {} (err) -> expect(err).to.equal er it 'a<b', do(er=new Error)-> system_test expect_err, a: mk_err er, {} b: mk 42, {da:'a'} (err) -> expect(err).to.equal er it 'e1<e2', do(e1=new Error, e2=new Error)-> system_test expect_err, b: mk_err e2, {da:'a'} a: mk_err e1, {} #shoould return first error in topo order (err) -> expect(err).to.equal e1 it 'a<er', do(er=new Error)-> system_test expect_err, a: mk_err er, {db:'b'} b: mk 42, {} (err) -> expect(err).to.equal er describe 'dependency named start', -> it 'system', -> fn = -> s.system start: mk 42, {} expect(fn).to.throw Error, /called \'start\'/ it 'subsystem', -> fn = -> s.subsystem start: 'dep', ->0 expect(fn).to.throw Error, /called \'start\'/ fn = -> do s.subsystem ok: 'start', ->0 expect(fn).to.throw Error, /called \'start\'/ it 'inject', -> fn = -> mk 42, {da:'start'} expect(fn).to.throw Error, /called \'start\'/ it 'rename from', -> fn = -> s.rename (mk 42, {da:'a'}), {start:'b'} expect(fn).to.throw Error, /called \'start\'/ it 'rename to', -> fn = -> s.rename (mk 42, {da:'a'}), {a:'start'} expect(fn).to.throw Error, /called \'start\'/ it 'fmap', -> fn = -> s.field 'start', 'start_here_is_fine' expect(fn).to.throw Error, /called \'start\'/ fn = -> s.field 'not_start', 'start' expect(fn).to.not.throw Error describe 'starting', -> it 'misses start method', -> cont = (err) -> if err then throw err fn = -> s.start {not_a_start:5}, cont expect(fn).to.throw Error, /No start method on system/ fn = -> s.start {start:5}, cont # not a fn expect(fn).to.throw Error, /No start method on system/ fn = -> s.start {}, cont expect(fn).to.throw Error, /No start method on system/ it 'unmet dependencies', system_test expect_err, a: s.inject 'unmet_dep' (err) -> do(fn=->throw err)-> expect(fn).to.throw Error, /unmet dependencies/ describe 'util', -> it 'field - not present', system_test expect_err, a: mk 5 b: s.field 'a', 'noy' (err) -> do(fn=->throw err)-> expect(fn).to.throw Error, /Field 'noy' not present./ it 'field - multi - not present', system_test expect_err, a: mk 5 b: s.field 'a', 'val', 'ge' (err) -> do(fn=->throw err)-> expect(fn).to.throw Error, /Field 'ge' not present./