synapse 2.173.1__py311-none-any.whl → 2.175.0__py311-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of synapse might be problematic. Click here for more details.

Files changed (54) hide show
  1. synapse/axon.py +1 -1
  2. synapse/common.py +19 -5
  3. synapse/cortex.py +46 -10
  4. synapse/daemon.py +11 -12
  5. synapse/lib/agenda.py +6 -0
  6. synapse/lib/ast.py +5 -1
  7. synapse/lib/cell.py +22 -13
  8. synapse/lib/jupyter.py +21 -0
  9. synapse/lib/layer.py +124 -30
  10. synapse/lib/link.py +3 -2
  11. synapse/lib/lmdbslab.py +11 -1
  12. synapse/lib/modelrev.py +31 -1
  13. synapse/lib/modules.py +1 -0
  14. synapse/lib/msgpack.py +25 -3
  15. synapse/lib/nexus.py +26 -22
  16. synapse/lib/schemas.py +31 -0
  17. synapse/lib/snap.py +13 -0
  18. synapse/lib/storm.py +103 -86
  19. synapse/lib/stormsvc.py +30 -11
  20. synapse/lib/stormtypes.py +23 -9
  21. synapse/lib/trigger.py +0 -4
  22. synapse/lib/types.py +1 -1
  23. synapse/lib/version.py +2 -2
  24. synapse/lib/view.py +2 -0
  25. synapse/models/crypto.py +22 -0
  26. synapse/models/economic.py +23 -2
  27. synapse/models/entity.py +16 -0
  28. synapse/models/files.py +4 -1
  29. synapse/models/geopol.py +3 -0
  30. synapse/models/orgs.py +3 -4
  31. synapse/tests/test_cortex.py +13 -0
  32. synapse/tests/test_daemon.py +36 -0
  33. synapse/tests/test_lib_agenda.py +129 -1
  34. synapse/tests/test_lib_ast.py +56 -0
  35. synapse/tests/test_lib_cell.py +11 -0
  36. synapse/tests/test_lib_grammar.py +4 -0
  37. synapse/tests/test_lib_httpapi.py +1 -0
  38. synapse/tests/test_lib_lmdbslab.py +16 -1
  39. synapse/tests/test_lib_modelrev.py +86 -0
  40. synapse/tests/test_lib_msgpack.py +58 -8
  41. synapse/tests/test_lib_nexus.py +44 -1
  42. synapse/tests/test_lib_storm.py +134 -18
  43. synapse/tests/test_lib_stormsvc.py +128 -51
  44. synapse/tests/test_lib_stormtypes.py +43 -4
  45. synapse/tests/test_lib_trigger.py +23 -4
  46. synapse/tests/test_model_crypto.py +6 -0
  47. synapse/tests/test_model_economic.py +14 -1
  48. synapse/tests/test_model_geopol.py +3 -0
  49. synapse/tools/changelog.py +256 -0
  50. {synapse-2.173.1.dist-info → synapse-2.175.0.dist-info}/METADATA +1 -1
  51. {synapse-2.173.1.dist-info → synapse-2.175.0.dist-info}/RECORD +54 -52
  52. {synapse-2.173.1.dist-info → synapse-2.175.0.dist-info}/WHEEL +1 -1
  53. {synapse-2.173.1.dist-info → synapse-2.175.0.dist-info}/LICENSE +0 -0
  54. {synapse-2.173.1.dist-info → synapse-2.175.0.dist-info}/top_level.txt +0 -0
@@ -17,6 +17,50 @@ class MsgPackTest(s_t_utils.SynTest):
17
17
  byts = s_msgpack._fallback_en(('hehe', 10))
18
18
  self.eq(byts, b'\x92\xa4hehe\n')
19
19
 
20
+ def test_msgpack_ext(self):
21
+ valu = 0xffffffffffffffffffffffffffffffff
22
+ item = ('woot', valu)
23
+ byts = s_msgpack.en(item)
24
+ self.eq(item, s_msgpack.un(byts))
25
+ self.eq(byts, s_msgpack._fallback_en(item))
26
+
27
+ unpk = s_msgpack.Unpk()
28
+ self.eq(((24, item),), unpk.feed(byts))
29
+ with self.raises(s_exc.SynErr):
30
+ s_msgpack._ext_un(99, b'red baloons')
31
+
32
+ # Negative number support as well.
33
+ negvalu = -1 * valu
34
+ negitem = ('woot', negvalu)
35
+ negbytes = s_msgpack.en(negitem)
36
+ self.eq(negitem, s_msgpack.un(negbytes))
37
+ self.eq(negbytes, s_msgpack._fallback_en(negitem))
38
+
39
+ # Check across item.bit_length() boundaries
40
+ v = 0xffffffffffffffff
41
+ for i in (1, 0xffffffffffffffff + 1, 0xffffffffffffffff + 2):
42
+ nv = v + i
43
+ buf = s_msgpack.en(nv)
44
+ self.eq(nv, s_msgpack.un(buf))
45
+ v = -0x8000000000000000
46
+ for i in (1, 0x7fffffffffffffff, 0x7fffffffffffffff + 1):
47
+ nv = v - i
48
+ buf = s_msgpack.en(nv)
49
+ self.eq(nv, s_msgpack.un(buf))
50
+
51
+ # We can also support values > 128 bits in width
52
+ valu = 0xfffffffffffffffffffffffffffffffff
53
+ item = ('woot', valu)
54
+ byts = s_msgpack.en(item)
55
+ self.eq(item, s_msgpack.un(byts))
56
+ self.eq(byts, s_msgpack._fallback_en(item))
57
+
58
+ negvalu = -1 * valu
59
+ negitem = ('woot', negvalu)
60
+ negbytes = s_msgpack.en(negitem)
61
+ self.eq(negitem, s_msgpack.un(negbytes))
62
+ self.eq(negbytes, s_msgpack._fallback_en(negitem))
63
+
20
64
  def test_msgpack_un(self):
21
65
  item = s_msgpack.un(b'\x92\xa4hehe\n')
22
66
  self.eq(item, ('hehe', 10))
@@ -106,8 +150,8 @@ class MsgPackTest(s_t_utils.SynTest):
106
150
  self.checkLoadfile(s_msgpack._fallback_en)
107
151
 
108
152
  def checkTypes(self, enfunc):
109
- # This is a future-proofing test for msgpack to ensure that
110
- buf = b'\x92\xa4hehe\x85\xa3str\xa41234\xa3int\xcd\x04\xd2\xa5float\xcb@(\xae\x14z\xe1G\xae\xa3bin\xc4\x041234\xa9realworld\xac\xc7\x8b\xef\xbf\xbd\xed\xa1\x82\xef\xbf\xbd\x12'
153
+ # This is a future-proofing test for msgpack to ensure that we have stability with msgpack-python
154
+ buf = b'\x92\xa4hehe\x8b\xa3str\xa41234\xa3int\xcd\x04\xd2\xa5float\xcb@(\xae\x14z\xe1G\xae\xa3bin\xc4\x041234\xa9realworld\xac\xc7\x8b\xef\xbf\xbd\xed\xa1\x82\xef\xbf\xbd\x12\xabalmostlarge\xcf\xff\xff\xff\xff\xff\xff\xff\xfe\xb1extlargeThreshold\xcf\xff\xff\xff\xff\xff\xff\xff\xff\xa8extlarge\xc7\t\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\xabalmostsmall\xd3\x80\x00\x00\x00\x00\x00\x00\x01\xb4almostsmallThreshold\xd3\x80\x00\x00\x00\x00\x00\x00\x00\xa8extsmall\xc7\t\x01\xff\x7f\xff\xff\xff\xff\xff\xff\xff'
111
155
  struct = (
112
156
  'hehe',
113
157
  {
@@ -115,7 +159,15 @@ class MsgPackTest(s_t_utils.SynTest):
115
159
  'int': 1234,
116
160
  'float': 12.34,
117
161
  'bin': b'1234',
118
- 'realworld': '\u01cb\ufffd\ud842\ufffd\u0012'
162
+ 'realworld': '\u01cb\ufffd\ud842\ufffd\u0012',
163
+ 'almostlarge': 0xffffffffffffffff - 1,
164
+ 'extlargeThreshold': 0xffffffffffffffff,
165
+ # extlarge is handled with our custom extension type
166
+ 'extlarge': 0xffffffffffffffff + 1,
167
+ 'almostsmall': -0x8000000000000000 + 1,
168
+ 'almostsmallThreshold': -0x8000000000000000,
169
+ # extsmall is handled with our custom extension type
170
+ 'extsmall': -0x8000000000000000 - 1,
119
171
  }
120
172
  )
121
173
  unode = s_msgpack.un(buf)
@@ -135,7 +187,7 @@ class MsgPackTest(s_t_utils.SynTest):
135
187
  unpk = s_msgpack.Unpk()
136
188
  objs = unpk.feed(buf)
137
189
  self.len(1, objs)
138
- self.eq(objs[0], (71, struct))
190
+ self.eq(objs[0], (212, struct))
139
191
 
140
192
  # Generic isok helper
141
193
  self.true(s_msgpack.isok(1))
@@ -148,6 +200,8 @@ class MsgPackTest(s_t_utils.SynTest):
148
200
  self.true(s_msgpack.isok([1]))
149
201
  self.true(s_msgpack.isok((1,)))
150
202
  self.true(s_msgpack.isok({1: 1}))
203
+ self.true(s_msgpack.isok(0xffffffffffffffff + 1))
204
+ self.true(s_msgpack.isok(-0x8000000000000000 - 1))
151
205
  # unpackage types
152
206
  self.false(s_msgpack.isok({1, 2})) # set
153
207
  self.false(s_msgpack.isok(print)) # function
@@ -198,10 +252,6 @@ class MsgPackTest(s_t_utils.SynTest):
198
252
  self.raises(s_exc.NotMsgpackSafe, enfunc, {1, 2})
199
253
  self.raises(s_exc.NotMsgpackSafe, enfunc, Exception())
200
254
  self.raises(s_exc.NotMsgpackSafe, enfunc, s_msgpack.en)
201
- # too long
202
- with self.raises(s_exc.NotMsgpackSafe) as cm:
203
- enfunc({'longlong': 45234928034723904723906})
204
- self.isin('OverflowError', cm.exception.get('mesg'))
205
255
 
206
256
  def test_msgpack_bad_types(self):
207
257
  self.checkBadTypes(s_msgpack.en)
@@ -3,7 +3,6 @@ from unittest import mock
3
3
 
4
4
  import synapse.exc as s_exc
5
5
  import synapse.common as s_common
6
- import synapse.cortex as s_cortex
7
6
 
8
7
  import synapse.lib.cell as s_cell
9
8
  import synapse.lib.nexus as s_nexus
@@ -302,3 +301,47 @@ class NexusTest(s_t_utils.SynTest):
302
301
  with self.raises(s_exc.IsReadOnly):
303
302
  await cell01.sync()
304
303
  self.isin(s_nexus.leaderversion, cell01.nexsroot.writeholds)
304
+
305
+ async def test_mirror_nexus_loop_failure(self):
306
+ with self.getTestDir() as dirn:
307
+
308
+ s_common.yamlsave({'nexslog:en': True}, dirn, 'cell.yaml')
309
+ async with await s_cell.Cell.anit(dirn=dirn) as cell00:
310
+
311
+ await cell00.runBackup(name='cell01')
312
+
313
+ path = s_common.genpath(dirn, 'backups', 'cell01')
314
+
315
+ conf = s_common.yamlload(path, 'cell.yaml')
316
+ conf['mirror'] = f'cell://{dirn}'
317
+ s_common.yamlsave(conf, path, 'cell.yaml')
318
+
319
+ seen = False
320
+ restarted = False
321
+ orig = s_nexus.NexsRoot.delWriteHold
322
+
323
+ # Patch NexsRoot.delWriteHold so we can cause an exception in
324
+ # the setup part of the nexus loop (NexsRoot.runMirrorLoop). The
325
+ # exception should only happen one time so we can check that the
326
+ # proxy and the nexus loop were both restarted
327
+ async def delWriteHold(self, reason):
328
+ nonlocal seen
329
+ nonlocal restarted
330
+ if not seen:
331
+ seen = True
332
+ raise Exception('Knock over the nexus setup.')
333
+
334
+ restarted = True
335
+ return await orig(self, reason)
336
+
337
+ with mock.patch('synapse.lib.nexus.NexsRoot.delWriteHold', delWriteHold):
338
+ with self.getLoggerStream('synapse.lib.nexus') as stream:
339
+ async with await s_cell.Cell.anit(dirn=path) as cell01:
340
+ await cell01.sync()
341
+
342
+ stream.seek(0)
343
+ data = stream.read()
344
+ mesg = 'Unknown error during mirror loop startup: Knock over the nexus setup.'
345
+ self.isin(mesg, data)
346
+
347
+ self.true(restarted)
@@ -1348,6 +1348,12 @@ class StormTest(s_t_utils.SynTest):
1348
1348
 
1349
1349
  self.len(0, await core.nodes('diff', opts=altview))
1350
1350
 
1351
+ await core.nodes('[ ps:contact=* :name=con0 +#con0 +#con0.foo +#conalt ]', opts=altview)
1352
+ await core.nodes('[ ps:contact=* :name=con1 +#con1 +#conalt ]', opts=altview)
1353
+
1354
+ nodes = await core.nodes('diff --tag conalt con1 con0.foo con0 newp', opts=altview)
1355
+ self.sorteq(['con0', 'con1'], [n.get('name') for n in nodes])
1356
+
1351
1357
  q = '''
1352
1358
  [ ou:name=foo +(bar)> {[ ou:name=bar ]} ]
1353
1359
  { for $i in $lib.range(1001) { $node.data.set($i, $i) }}
@@ -1537,6 +1543,112 @@ class StormTest(s_t_utils.SynTest):
1537
1543
  await core.nodes('inet:ssl:cert | merge --apply', opts=altview)
1538
1544
  self.len(1, await core.nodes('inet:ipv4'))
1539
1545
 
1546
+ async def test_storm_merge_perms(self):
1547
+
1548
+ async with self.getTestCore() as core:
1549
+
1550
+ await core.addTagProp('score', ('int', {}), {})
1551
+
1552
+ visi = await core.auth.addUser('visi')
1553
+ opts = {'user': visi.iden}
1554
+
1555
+ view2 = await core.callStorm('return($lib.view.get().fork())')
1556
+ view2opts = opts | {'view': view2['iden']}
1557
+ layr2 = view2['layers'][0]['iden']
1558
+ layr1 = view2['layers'][1]['iden']
1559
+
1560
+ await visi.addRule((True, ('view',)))
1561
+ await visi.addRule((True, ('node', 'add')), gateiden=layr2)
1562
+ await visi.addRule((True, ('node', 'prop', 'set')), gateiden=layr2)
1563
+ await visi.addRule((True, ('node', 'tag', 'add')), gateiden=layr2)
1564
+ await visi.addRule((True, ('node', 'data', 'set')), gateiden=layr2)
1565
+ await visi.addRule((True, ('node', 'edge', 'add')), gateiden=layr2)
1566
+
1567
+ await core.nodes('[ ou:name=test ]')
1568
+
1569
+ await core.nodes('''
1570
+ [ ps:contact=*
1571
+ :name=test0
1572
+ +(test)> { ou:name=test }
1573
+ +#test1.foo=now
1574
+ +#test2
1575
+ +#test3:score=42
1576
+ ]
1577
+ $node.data.set(foo, bar)
1578
+ ''', opts=view2opts)
1579
+
1580
+ with self.raises(s_exc.AuthDeny) as ecm:
1581
+ await core.nodes('ps:contact merge --apply', opts=view2opts)
1582
+ self.eq('node.del.ps:contact', ecm.exception.errinfo['perm'])
1583
+ await visi.addRule((True, ('node', 'del')), gateiden=layr2)
1584
+
1585
+ with self.raises(s_exc.AuthDeny) as ecm:
1586
+ await core.nodes('ps:contact merge --apply', opts=view2opts)
1587
+ self.eq('node.add.ps:contact', ecm.exception.errinfo['perm'])
1588
+ await visi.addRule((True, ('node', 'add')), gateiden=layr1)
1589
+
1590
+ with self.raises(s_exc.AuthDeny) as ecm:
1591
+ await core.nodes('ps:contact merge --apply', opts=view2opts)
1592
+ self.eq('node.prop.del.ps:contact..created', ecm.exception.errinfo['perm'])
1593
+ await visi.addRule((True, ('node', 'prop', 'del')), gateiden=layr2)
1594
+
1595
+ with self.raises(s_exc.AuthDeny) as ecm:
1596
+ await core.nodes('ps:contact merge --apply', opts=view2opts)
1597
+ self.eq('node.prop.set.ps:contact..created', ecm.exception.errinfo['perm'])
1598
+ await visi.addRule((True, ('node', 'prop', 'set')), gateiden=layr1)
1599
+
1600
+ with self.raises(s_exc.AuthDeny) as ecm:
1601
+ await core.nodes('ps:contact merge --apply', opts=view2opts)
1602
+ self.eq('node.tag.del.test1.foo', ecm.exception.errinfo['perm'])
1603
+ await visi.addRule((True, ('node', 'tag', 'del', 'test1', 'foo')), gateiden=layr2)
1604
+
1605
+ with self.raises(s_exc.AuthDeny) as ecm:
1606
+ await core.nodes('ps:contact merge --apply', opts=view2opts)
1607
+ self.eq('node.tag.add.test1.foo', ecm.exception.errinfo['perm'])
1608
+ await visi.addRule((True, ('node', 'tag', 'add', 'test1', 'foo')), gateiden=layr1)
1609
+
1610
+ with self.raises(s_exc.AuthDeny) as ecm:
1611
+ await core.nodes('ps:contact merge --apply', opts=view2opts)
1612
+ self.eq('node.tag.del.test3', ecm.exception.errinfo['perm'])
1613
+ await visi.addRule((True, ('node', 'tag', 'del', 'test3')), gateiden=layr2)
1614
+
1615
+ with self.raises(s_exc.AuthDeny) as ecm:
1616
+ await core.nodes('ps:contact merge --apply', opts=view2opts)
1617
+ self.eq('node.tag.add.test3', ecm.exception.errinfo['perm'])
1618
+ await visi.addRule((True, ('node', 'tag', 'add', 'test3')), gateiden=layr1)
1619
+
1620
+ with self.raises(s_exc.AuthDeny) as ecm:
1621
+ await core.nodes('ps:contact merge --apply', opts=view2opts)
1622
+ self.eq('node.tag.del.test2', ecm.exception.errinfo['perm'])
1623
+ await visi.addRule((True, ('node', 'tag', 'del', 'test2')), gateiden=layr2)
1624
+
1625
+ with self.raises(s_exc.AuthDeny) as ecm:
1626
+ await core.nodes('ps:contact merge --apply', opts=view2opts)
1627
+ self.eq('node.tag.add.test2', ecm.exception.errinfo['perm'])
1628
+ await visi.addRule((True, ('node', 'tag', 'add', 'test2')), gateiden=layr1)
1629
+
1630
+ with self.raises(s_exc.AuthDeny) as ecm:
1631
+ await core.nodes('ps:contact merge --apply', opts=view2opts)
1632
+ self.eq('node.data.pop.foo', ecm.exception.errinfo['perm'])
1633
+ await visi.addRule((True, ('node', 'data', 'pop')), gateiden=layr2)
1634
+
1635
+ with self.raises(s_exc.AuthDeny) as ecm:
1636
+ await core.nodes('ps:contact merge --apply', opts=view2opts)
1637
+ self.eq('node.data.set.foo', ecm.exception.errinfo['perm'])
1638
+ await visi.addRule((True, ('node', 'data', 'set')), gateiden=layr1)
1639
+
1640
+ with self.raises(s_exc.AuthDeny) as ecm:
1641
+ await core.nodes('ps:contact merge --apply', opts=view2opts)
1642
+ self.eq('node.edge.del.test', ecm.exception.errinfo['perm'])
1643
+ await visi.addRule((True, ('node', 'edge', 'del')), gateiden=layr2)
1644
+
1645
+ with self.raises(s_exc.AuthDeny) as ecm:
1646
+ await core.nodes('ps:contact merge --apply', opts=view2opts)
1647
+ self.eq('node.edge.add.test', ecm.exception.errinfo['perm'])
1648
+ await visi.addRule((True, ('node', 'edge', 'add')), gateiden=layr1)
1649
+
1650
+ await core.nodes('ps:contact merge --apply', opts=view2opts)
1651
+
1540
1652
  async def test_storm_movenodes(self):
1541
1653
 
1542
1654
  async with self.getTestCore() as core:
@@ -2283,9 +2395,9 @@ class StormTest(s_t_utils.SynTest):
2283
2395
  msgs = await core.stormlist(f'pkg.load --ssl-noverify https://127.0.0.1:{port}/api/v1/pkgtest/notok')
2284
2396
  self.stormIsInWarn('pkg.load got JSON error: FooBar', msgs)
2285
2397
 
2286
- replay = s_common.envbool('SYNDEV_NEXUS_REPLAY')
2287
- nevents = 4 if replay else 2
2288
- waiter = core.waiter(nevents, 'core:pkg:onload:complete')
2398
+ # onload will on fire once. all other pkg.load events will effectively bounce
2399
+ # because the pkg hasn't changed so no loading occurs
2400
+ waiter = core.waiter(1, 'core:pkg:onload:complete')
2289
2401
 
2290
2402
  with self.getAsyncLoggerStream('synapse.cortex') as stream:
2291
2403
  msgs = await core.stormlist(f'pkg.load --ssl-noverify https://127.0.0.1:{port}/api/v1/pkgtest/yep')
@@ -2301,10 +2413,10 @@ class StormTest(s_t_utils.SynTest):
2301
2413
  self.isin("No var with name: newp", buf)
2302
2414
  self.len(1, await core.nodes(f'ps:contact={cont}'))
2303
2415
 
2304
- evnts = await waiter.wait(timeout=2)
2305
- exp = []
2306
- for _ in range(nevents):
2307
- exp.append(('core:pkg:onload:complete', {'pkg': 'testload'}))
2416
+ evnts = await waiter.wait(timeout=4)
2417
+ exp = [
2418
+ ('core:pkg:onload:complete', {'pkg': 'testload'})
2419
+ ]
2308
2420
  self.eq(exp, evnts)
2309
2421
 
2310
2422
  async def test_storm_tree(self):
@@ -2912,27 +3024,30 @@ class StormTest(s_t_utils.SynTest):
2912
3024
 
2913
3025
  q = 'inet:ipv4=1.2.3.4 | tee --join { -> * } { <- * }'
2914
3026
  nodes = await core.nodes(q)
2915
- self.len(3, nodes)
3027
+ self.len(4, nodes)
2916
3028
  self.eq(nodes[0].ndef, ('inet:asn', 0))
2917
3029
  self.eq(nodes[1].ndef[0], ('inet:dns:a'))
2918
- self.eq(nodes[2].ndef, ('inet:ipv4', 0x01020304))
3030
+ self.eq(nodes[2].ndef[0], ('edge:refs'))
3031
+ self.eq(nodes[3].ndef, ('inet:ipv4', 0x01020304))
2919
3032
 
2920
3033
  q = 'inet:ipv4=1.2.3.4 | tee --join { -> * } { <- * } { -> edge:refs:n2 :n1 -> * }'
2921
3034
  nodes = await core.nodes(q)
2922
- self.len(4, nodes)
3035
+ self.len(5, nodes)
2923
3036
  self.eq(nodes[0].ndef, ('inet:asn', 0))
2924
3037
  self.eq(nodes[1].ndef[0], ('inet:dns:a'))
2925
- self.eq(nodes[2].ndef[0], ('media:news'))
2926
- self.eq(nodes[3].ndef, ('inet:ipv4', 0x01020304))
3038
+ self.eq(nodes[2].ndef[0], ('edge:refs'))
3039
+ self.eq(nodes[3].ndef[0], ('media:news'))
3040
+ self.eq(nodes[4].ndef, ('inet:ipv4', 0x01020304))
2927
3041
 
2928
3042
  # Queries can be a heavy list
2929
3043
  q = '$list = $lib.list(${ -> * }, ${ <- * }, ${ -> edge:refs:n2 :n1 -> * }) inet:ipv4=1.2.3.4 | tee --join $list'
2930
3044
  nodes = await core.nodes(q)
2931
- self.len(4, nodes)
3045
+ self.len(5, nodes)
2932
3046
  self.eq(nodes[0].ndef, ('inet:asn', 0))
2933
3047
  self.eq(nodes[1].ndef[0], ('inet:dns:a'))
2934
- self.eq(nodes[2].ndef[0], ('media:news'))
2935
- self.eq(nodes[3].ndef, ('inet:ipv4', 0x01020304))
3048
+ self.eq(nodes[2].ndef[0], ('edge:refs'))
3049
+ self.eq(nodes[3].ndef[0], ('media:news'))
3050
+ self.eq(nodes[4].ndef, ('inet:ipv4', 0x01020304))
2936
3051
 
2937
3052
  # A empty list of queries still works as an nop
2938
3053
  q = '$list = $lib.list() | tee $list'
@@ -2959,11 +3074,12 @@ class StormTest(s_t_utils.SynTest):
2959
3074
  q = 'inet:ipv4=1.2.3.4 | tee --join $list'
2960
3075
  queries = ('-> *', '<- *', '-> edge:refs:n2 :n1 -> *')
2961
3076
  nodes = await core.nodes(q, {'vars': {'list': queries}})
2962
- self.len(4, nodes)
3077
+ self.len(5, nodes)
2963
3078
  self.eq(nodes[0].ndef, ('inet:asn', 0))
2964
3079
  self.eq(nodes[1].ndef[0], ('inet:dns:a'))
2965
- self.eq(nodes[2].ndef[0], ('media:news'))
2966
- self.eq(nodes[3].ndef, ('inet:ipv4', 0x01020304))
3080
+ self.eq(nodes[2].ndef[0], ('edge:refs'))
3081
+ self.eq(nodes[3].ndef[0], ('media:news'))
3082
+ self.eq(nodes[4].ndef, ('inet:ipv4', 0x01020304))
2967
3083
 
2968
3084
  # Empty queries are okay - they will just return the input node
2969
3085
  q = 'inet:ipv4=1.2.3.4 | tee {}'
@@ -2,7 +2,6 @@ import asyncio
2
2
  import contextlib
3
3
  import synapse.exc as s_exc
4
4
  import synapse.common as s_common
5
- import synapse.cortex as s_cortex
6
5
 
7
6
  import synapse.tests.utils as s_test
8
7
 
@@ -796,56 +795,134 @@ class StormSvcTest(s_test.SynTest):
796
795
 
797
796
  with self.getTestDir() as dirn:
798
797
  async with self.getTestCore(dirn=dirn) as core:
799
- with self.getTestDir() as svcd:
800
- async with await ChangingService.anit(svcd) as chng:
801
- chng.dmon.share('chng', chng)
802
-
803
- root = await chng.auth.getUserByName('root')
804
- await root.setPasswd('root')
805
-
806
- info = await chng.dmon.listen('tcp://127.0.0.1:0/')
807
- host, port = info
808
-
809
- curl = f'tcp://root:root@127.0.0.1:{port}/chng'
810
-
811
- await core.nodes(f'service.add chng {curl}')
812
- await core.nodes('$lib.service.wait(chng)')
813
-
814
- self.nn(core.getStormCmd('oldcmd'))
815
- self.nn(core.getStormCmd('old.bar'))
816
- self.nn(core.getStormCmd('old.baz'))
817
- self.none(core.getStormCmd('new.baz'))
818
- self.none(core.getStormCmd('runtecho'))
819
- self.none(core.getStormCmd('newcmd'))
820
- self.isin('old', core.stormpkgs)
821
- self.isin('old.bar', core.stormmods)
822
- self.isin('old.baz', core.stormmods)
823
- pkg = await core.getStormPkg('old')
824
- self.eq(pkg.get('version'), '0.0.1')
825
-
826
- waiter = core.waiter(1, 'stormsvc:client:unready')
827
-
828
- self.true(await waiter.wait(10))
829
- async with await ChangingService.anit(svcd, {'updated': True}) as chng:
830
- chng.dmon.share('chng', chng)
831
- await chng.dmon.listen(f'tcp://127.0.0.1:{port}/')
832
-
833
- await core.nodes('$lib.service.wait(chng)')
834
-
835
- self.nn(core.getStormCmd('newcmd'))
836
- self.nn(core.getStormCmd('new.baz'))
837
- self.nn(core.getStormCmd('old.bar'))
838
- self.nn(core.getStormCmd('runtecho'))
839
- self.none(core.getStormCmd('oldcmd'))
840
- self.none(core.getStormCmd('old.baz'))
841
- self.isin('old', core.stormpkgs)
842
- self.isin('new', core.stormpkgs)
843
- self.isin('echo', core.stormmods)
844
- self.isin('old.bar', core.stormmods)
845
- self.isin('new.baz', core.stormmods)
846
- self.notin('old.baz', core.stormmods)
847
- pkg = await core.getStormPkg('old')
848
- self.eq(pkg.get('version'), '0.1.0')
798
+ async with core.beholder() as wind:
799
+ with self.getTestDir() as svcd:
800
+ async with await ChangingService.anit(svcd) as chng:
801
+ chng.dmon.share('chng', chng)
802
+
803
+ root = await chng.auth.getUserByName('root')
804
+ await root.setPasswd('root')
805
+
806
+ info = await chng.dmon.listen('tcp://127.0.0.1:0/')
807
+ host, port = info
808
+
809
+ curl = f'tcp://root:root@127.0.0.1:{port}/chng'
810
+
811
+ await core.nodes(f'service.add chng {curl}')
812
+ await core.nodes('$lib.service.wait(chng)')
813
+
814
+ self.nn(core.getStormCmd('oldcmd'))
815
+ self.nn(core.getStormCmd('old.bar'))
816
+ self.nn(core.getStormCmd('old.baz'))
817
+ self.none(core.getStormCmd('new.baz'))
818
+ self.none(core.getStormCmd('runtecho'))
819
+ self.none(core.getStormCmd('newcmd'))
820
+ self.isin('old', core.stormpkgs)
821
+ self.isin('old.bar', core.stormmods)
822
+ self.isin('old.baz', core.stormmods)
823
+ pkg = await core.getStormPkg('old')
824
+ self.eq(pkg.get('version'), '0.0.1')
825
+
826
+ waiter = core.waiter(1, 'stormsvc:client:unready')
827
+
828
+ self.true(await waiter.wait(10))
829
+ async with await ChangingService.anit(svcd, {'updated': True}) as chng:
830
+ chng.dmon.share('chng', chng)
831
+ await chng.dmon.listen(f'tcp://127.0.0.1:{port}/')
832
+
833
+ await core.nodes('$lib.service.wait(chng)')
834
+
835
+ self.nn(core.getStormCmd('newcmd'))
836
+ self.nn(core.getStormCmd('new.baz'))
837
+ self.nn(core.getStormCmd('old.bar'))
838
+ self.nn(core.getStormCmd('runtecho'))
839
+ self.none(core.getStormCmd('oldcmd'))
840
+ self.none(core.getStormCmd('old.baz'))
841
+ self.isin('old', core.stormpkgs)
842
+ self.isin('new', core.stormpkgs)
843
+ self.isin('echo', core.stormmods)
844
+ self.isin('old.bar', core.stormmods)
845
+ self.isin('new.baz', core.stormmods)
846
+ self.notin('old.baz', core.stormmods)
847
+ pkg = await core.getStormPkg('old')
848
+ self.eq(pkg.get('version'), '0.1.0')
849
+
850
+ async with await ChangingService.anit(svcd, {'updated': False}) as chng:
851
+ chng.dmon.share('chng', chng)
852
+ await chng.dmon.listen(f'tcp://127.0.0.1:{port}/')
853
+
854
+ await core.nodes('$lib.service.wait(chng)')
855
+ self.nn(core.getStormCmd('oldcmd'))
856
+ self.nn(core.getStormCmd('old.bar'))
857
+ self.nn(core.getStormCmd('old.baz'))
858
+ self.none(core.getStormCmd('new.baz'))
859
+ self.none(core.getStormCmd('runtecho'))
860
+ self.none(core.getStormCmd('newcmd'))
861
+ self.isin('old', core.stormpkgs)
862
+ self.isin('old.bar', core.stormmods)
863
+ self.isin('old.baz', core.stormmods)
864
+
865
+ self.none(await core.getStormPkg('new'))
866
+
867
+ pkg = await core.getStormPkg('old')
868
+ self.eq(pkg.get('version'), '0.0.1')
869
+
870
+ svcs = await core.callStorm('return($lib.service.list())')
871
+ self.len(1, svcs)
872
+
873
+ async with await ChangingService.anit(svcd, {'updated': True}) as chng:
874
+ chng.dmon.share('chng', chng)
875
+ await chng.dmon.listen(f'tcp://127.0.0.1:{port}/')
876
+
877
+ await core.nodes('$lib.service.wait(chng)')
878
+
879
+ async with await ChangingService.anit(svcd, {'updated': True}) as chng:
880
+ chng.dmon.share('chng', chng)
881
+ await chng.dmon.listen(f'tcp://127.0.0.1:{port}/')
882
+
883
+ await core.nodes('$lib.service.wait(chng)')
884
+
885
+ events = []
886
+ async for m in wind:
887
+ events.append(m)
888
+
889
+ self.len(16, events)
890
+
891
+ # updated = false
892
+ self.eq('svc:set', events[-9]['event'])
893
+ self.eq('chng', events[-9]['info']['name'])
894
+ self.eq((0, 0, 1), events[-9]['info']['version'])
895
+
896
+ self.eq('pkg:del', events[-8]['event'])
897
+ self.eq('old', events[-8]['info']['name'])
898
+
899
+ self.eq('pkg:add', events[-7]['event'])
900
+ self.eq('old', events[-7]['info']['name'])
901
+ self.eq('0.0.1', events[-7]['info']['version'])
902
+
903
+ self.eq('pkg:del', events[-6]['event'])
904
+ self.eq('new', events[-6]['info']['name'])
905
+
906
+ # updated = true
907
+ self.eq('svc:set', events[-5]['event'])
908
+ self.eq('chng', events[-5]['info']['name'])
909
+ self.eq((0, 0, 1), events[-5]['info']['version'])
910
+
911
+ self.eq('pkg:del', events[-4]['event'])
912
+ self.eq('old', events[-4]['info']['name'])
913
+
914
+ self.eq('pkg:add', events[-3]['event'])
915
+ self.eq('old', events[-3]['info']['name'])
916
+ self.eq('0.1.0', events[-3]['info']['version'])
917
+
918
+ self.eq('pkg:add', events[-2]['event'])
919
+ self.eq('new', events[-2]['info']['name'])
920
+
921
+ # we get the set to let us know things are back, not no adds since the pkgs are the same
922
+ # so this is the last
923
+ self.eq('svc:set', events[-1]['event'])
924
+ self.eq('chng', events[-1]['info']['name'])
925
+ self.eq((0, 0, 1), events[-1]['info']['version'])
849
926
 
850
927
  # This test verifies that storm commands loaded from a previously connected service are still available,
851
928
  # even if the service is not available now
@@ -322,6 +322,42 @@ class StormTypesTest(s_test.SynTest):
322
322
  msgs = await core.stormlist('$lib.debug = (1) hehe.haha')
323
323
  self.stormIsInPrint('hehe.haha', msgs)
324
324
 
325
+ async def test_storm_doubleadd_pkg(self):
326
+ async with self.getTestCore() as core:
327
+ async with core.beholder() as wind:
328
+ pkg = {
329
+ 'name': 'hehe',
330
+ 'version': '1.1.1',
331
+ 'modules': [
332
+ {'name': 'hehe', 'storm': 'function getDebug() { return($lib.debug) }'},
333
+ ],
334
+ 'commands': [
335
+ {'name': 'hehe.haha', 'storm': 'if $lib.debug { $lib.print(hehe.haha) }'},
336
+ ],
337
+ }
338
+
339
+ # all but the first of these should bounce
340
+ for i in range(5):
341
+ await core.addStormPkg(pkg)
342
+
343
+ pkg['version'] = '1.2.3'
344
+
345
+ # all but the first of these should bounce
346
+ for i in range(5):
347
+ await core.addStormPkg(pkg)
348
+
349
+ events = []
350
+ async for m in wind:
351
+ events.append(m)
352
+ self.len(2, events)
353
+ self.eq('pkg:add', events[0]['event'])
354
+ self.eq('hehe', events[0]['info']['name'])
355
+ self.eq('1.1.1', events[0]['info']['version'])
356
+
357
+ self.eq('pkg:add', events[1]['event'])
358
+ self.eq('hehe', events[1]['info']['name'])
359
+ self.eq('1.2.3', events[1]['info']['version'])
360
+
325
361
  async def test_storm_private(self):
326
362
  async with self.getTestCore() as core:
327
363
  await core.addStormPkg({
@@ -4642,6 +4678,11 @@ class StormTypesTest(s_test.SynTest):
4642
4678
 
4643
4679
  opts = {'vars': {'iden': iden0}}
4644
4680
 
4681
+ # for coverage...
4682
+ self.false(await core.killCronTask('newp'))
4683
+ self.false(await core._killCronTask('newp'))
4684
+ self.false(await core.callStorm(f'return($lib.cron.get({iden0}).kill())'))
4685
+
4645
4686
  cdef = await core.callStorm('return($lib.cron.get($iden).pack())', opts=opts)
4646
4687
  self.eq('mydoc', cdef.get('doc'))
4647
4688
  self.eq('myname', cdef.get('name'))
@@ -4793,12 +4834,10 @@ class StormTypesTest(s_test.SynTest):
4793
4834
  self.stormIsInErr('does not match', mesgs)
4794
4835
 
4795
4836
  # Make sure the old one didn't run and the new query ran
4837
+ nextlayroffs = await layr.getEditOffs() + 1
4796
4838
  unixtime += 60
4797
- await asyncio.sleep(0)
4839
+ await layr.waitEditOffs(nextlayroffs, timeout=5)
4798
4840
  self.eq(1, await prox.count('meta:note:type=m1'))
4799
- # UNG WTF
4800
- await asyncio.sleep(0)
4801
- await asyncio.sleep(0)
4802
4841
  self.eq(1, await prox.count('meta:note:type=m2'))
4803
4842
 
4804
4843
  # Delete the job