synapse 2.213.0__py311-none-any.whl → 2.215.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 (76) hide show
  1. synapse/cortex.py +37 -6
  2. synapse/daemon.py +6 -6
  3. synapse/exc.py +13 -1
  4. synapse/lib/aha.py +5 -0
  5. synapse/lib/ast.py +2 -6
  6. synapse/lib/boss.py +47 -2
  7. synapse/lib/cell.py +193 -3
  8. synapse/lib/certdir.py +44 -1
  9. synapse/lib/cmd.py +24 -0
  10. synapse/lib/coro.py +8 -2
  11. synapse/lib/drive.py +7 -2
  12. synapse/lib/jsonstor.py +4 -1
  13. synapse/lib/layer.py +3 -1
  14. synapse/lib/link.py +11 -3
  15. synapse/lib/schemas.py +1 -1
  16. synapse/lib/snap.py +76 -65
  17. synapse/lib/storm.py +2 -1
  18. synapse/lib/stormlib/imap.py +3 -2
  19. synapse/lib/stormlib/spooled.py +1 -0
  20. synapse/lib/task.py +1 -0
  21. synapse/lib/version.py +2 -2
  22. synapse/models/inet.py +5 -0
  23. synapse/models/infotech.py +45 -0
  24. synapse/tests/files/testpkg_build_docs/docs/bar.rst +15 -0
  25. synapse/tests/files/testpkg_build_docs/docs/foo.rst +4 -0
  26. synapse/tests/files/testpkg_build_docs/storm/commands/testcmd.storm +0 -0
  27. synapse/tests/files/testpkg_build_docs/storm/modules/apimod.storm +0 -0
  28. synapse/tests/files/testpkg_build_docs/storm/modules/testmod.storm +0 -0
  29. synapse/tests/files/testpkg_build_docs/storm/testcmd.storm +5 -0
  30. synapse/tests/files/testpkg_build_docs/testpkg.yaml +69 -0
  31. synapse/tests/test_cortex.py +20 -1
  32. synapse/tests/test_daemon.py +1 -1
  33. synapse/tests/test_exc.py +6 -0
  34. synapse/tests/test_lib_ast.py +69 -14
  35. synapse/tests/test_lib_boss.py +8 -0
  36. synapse/tests/test_lib_cell.py +104 -5
  37. synapse/tests/test_lib_certdir.py +8 -0
  38. synapse/tests/test_lib_coro.py +5 -0
  39. synapse/tests/test_lib_httpapi.py +10 -2
  40. synapse/tests/test_lib_jsonstor.py +45 -0
  41. synapse/tests/test_lib_layer.py +10 -0
  42. synapse/tests/test_lib_link.py +1 -1
  43. synapse/tests/test_lib_storm.py +121 -1
  44. synapse/tests/test_lib_stormlib_iters.py +1 -1
  45. synapse/tests/test_lib_stormlib_spooled.py +20 -0
  46. synapse/tests/test_lib_stormtypes.py +15 -0
  47. synapse/tests/test_lib_types.py +5 -1
  48. synapse/tests/test_model_inet.py +7 -0
  49. synapse/tests/test_model_infotech.py +31 -0
  50. synapse/tests/test_telepath.py +32 -5
  51. synapse/tests/test_tools_axon.py +304 -0
  52. synapse/tests/test_tools_cortex_layer.py +419 -0
  53. synapse/tests/test_tools_demote.py +114 -0
  54. synapse/tests/test_tools_pkgs_gendocs.py +100 -0
  55. synapse/tests/test_tools_shutdown.py +95 -0
  56. synapse/tests/test_utils.py +22 -1
  57. synapse/tests/utils.py +44 -29
  58. synapse/tools/aha/easycert.py +2 -0
  59. synapse/tools/aha/enroll.py +3 -0
  60. synapse/tools/axon/__init__.py +0 -0
  61. synapse/tools/axon/dump.py +155 -0
  62. synapse/tools/axon/load.py +89 -0
  63. synapse/tools/cortex/__init__.py +0 -0
  64. synapse/tools/cortex/layer/__init__.py +0 -0
  65. synapse/tools/cortex/layer/dump.py +184 -0
  66. synapse/tools/cortex/layer/load.py +129 -0
  67. synapse/tools/demote.py +52 -0
  68. synapse/tools/healthcheck.py +1 -1
  69. synapse/tools/pkgs/gendocs.py +176 -0
  70. synapse/tools/pkgs/pandoc_filter.py +79 -0
  71. synapse/tools/shutdown.py +52 -0
  72. {synapse-2.213.0.dist-info → synapse-2.215.0.dist-info}/METADATA +1 -1
  73. {synapse-2.213.0.dist-info → synapse-2.215.0.dist-info}/RECORD +76 -53
  74. {synapse-2.213.0.dist-info → synapse-2.215.0.dist-info}/WHEEL +0 -0
  75. {synapse-2.213.0.dist-info → synapse-2.215.0.dist-info}/licenses/LICENSE +0 -0
  76. {synapse-2.213.0.dist-info → synapse-2.215.0.dist-info}/top_level.txt +0 -0
@@ -98,7 +98,7 @@ class DaemonTest(s_t_utils.SynTest):
98
98
 
99
99
  # Invalid data casues a link to fail on rx
100
100
  async with await prox.getPoolLink() as link:
101
- with self.getAsyncLoggerStream('synapse.lib.link', 'rx error') as stream:
101
+ with self.getAsyncLoggerStream('synapse.lib.link', 'rx closed unexpectedly') as stream:
102
102
  byts = b'\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03\xa6\xa3D\xd5\xdf%\xac\xa9\x92\xc3'
103
103
  await link.send(byts)
104
104
  self.true(await stream.wait(timeout=6))
synapse/tests/test_exc.py CHANGED
@@ -54,3 +54,9 @@ class ExcTest(s_t_utils.SynTest):
54
54
 
55
55
  with self.raises(s_exc.BadArg):
56
56
  s_exc.StormRaise(mesg='newp')
57
+
58
+ async def test_reprexc(self):
59
+ exc = s_exc.SynErr(mesg='woot')
60
+ self.eq('woot', s_exc.reprexc(exc))
61
+ self.eq('ValueError()', s_exc.reprexc(ValueError()))
62
+ self.eq("ValueError('woot')", s_exc.reprexc(ValueError('woot')))
@@ -2192,17 +2192,24 @@ class AstTest(s_test.SynTest):
2192
2192
  self.eq(nodes[1].ndef, ('test:str', 'init2'))
2193
2193
  self.eq(nodes[1].get('hehe'), 'hi')
2194
2194
 
2195
- # Non-runtsafe init fails to execute
2195
+ # Non-runtsafe values allowed in init
2196
+ q = '''
2197
+ init {
2198
+ [test:str=cool :hehe=runtsafety]
2199
+ $lib.print(:hehe)
2200
+ }
2201
+ '''
2202
+ msgs = await core.stormlist(q)
2203
+ self.stormIsInPrint('runtsafety', msgs)
2204
+
2205
+ # Non-runtsafe init doesn't create the node
2196
2206
  q = '''
2197
2207
  test:str^=init +:hehe $hehe=:hehe
2198
2208
  init {
2199
2209
  [test:str=$hehe]
2200
2210
  }
2201
2211
  '''
2202
- msgs = await core.stormlist(q)
2203
- erfo = [m for m in msgs if m[0] == 'err'][0]
2204
- self.eq(erfo[1][0], 'StormRuntimeError')
2205
- self.eq(erfo[1][1].get('mesg'), 'Init block query must be runtsafe')
2212
+ self.len(2, await core.nodes(q))
2206
2213
 
2207
2214
  # Runtsafe init works and can yield nodes, this has inbound nodes as well
2208
2215
  q = '''
@@ -2231,7 +2238,7 @@ class AstTest(s_test.SynTest):
2231
2238
  self.eq(nodes[1].ndef, ('test:str', 'fini2'))
2232
2239
  self.eq(nodes[1].get('hehe'), 'hehe')
2233
2240
 
2234
- # Non-runtsafe fini example which fails
2241
+ # Non-runtsafe fini example
2235
2242
  q = '''
2236
2243
  [test:str=fini3 :hehe="number3"]
2237
2244
  $hehe=:hehe
@@ -2239,10 +2246,10 @@ class AstTest(s_test.SynTest):
2239
2246
  [(test:str=fini4 :hehe=$hehe)]
2240
2247
  }
2241
2248
  '''
2242
- msgs = await core.stormlist(q)
2243
- erfo = [m for m in msgs if m[0] == 'err'][0]
2244
- self.eq(erfo[1][0], 'StormRuntimeError')
2245
- self.eq(erfo[1][1].get('mesg'), 'Fini block query must be runtsafe')
2249
+ nodes = await core.nodes(q)
2250
+ self.len(2, nodes)
2251
+ for node in nodes:
2252
+ self.eq('number3', node.get('hehe'))
2246
2253
 
2247
2254
  # Tally use - case example for counting
2248
2255
  q = '''
@@ -2413,12 +2420,21 @@ class AstTest(s_test.SynTest):
2413
2420
  self.stormIsInPrint('blorp', msgs)
2414
2421
 
2415
2422
  q = '''
2416
- [test:str=latte :hehe=milk] $beep=:hehe | spin | empty { $lib.print($beep) }
2423
+ [test:str=latte :hehe=milk] $beep=:hehe | spin | empty { $lib.print($beep) $lib.print(ok) }
2417
2424
  '''
2418
2425
  msgs = await core.stormlist(q)
2419
2426
  nodes = [m[1] for m in msgs if m[0] == 'node']
2420
2427
  self.len(0, nodes)
2421
- self.stormIsInErr('Empty block query must be runtsafe', msgs)
2428
+ self.stormIsInPrint('ok', msgs)
2429
+ self.stormNotInPrint('milk', msgs)
2430
+
2431
+ q = '''
2432
+ empty { [test:str=runtsafety :hehe=optional] $beep=:hehe $lib.print($beep) }
2433
+ '''
2434
+ msgs = await core.stormlist(q)
2435
+ nodes = [m[1] for m in msgs if m[0] == 'node']
2436
+ self.len(1, nodes)
2437
+ self.stormIsInPrint('optional', msgs)
2422
2438
 
2423
2439
  q = '''
2424
2440
  function foo() {
@@ -3224,11 +3240,11 @@ class AstTest(s_test.SynTest):
3224
3240
  off, end = errm[1][1]['highlight']['offsets']
3225
3241
  self.eq(':foo:bar', text[off:end])
3226
3242
 
3227
- text = 'init { $foo = :bar }'
3243
+ text = 'init { $foo = $bar }'
3228
3244
  msgs = await core.stormlist(text)
3229
3245
  errm = [m for m in msgs if m[0] == 'err'][0]
3230
3246
  off, end = errm[1][1]['highlight']['offsets']
3231
- self.eq(':bar', text[off:end])
3247
+ self.eq('bar', text[off:end])
3232
3248
 
3233
3249
  text = 'inet:ipv5'
3234
3250
  msgs = await core.stormlist(text)
@@ -3554,6 +3570,45 @@ class AstTest(s_test.SynTest):
3554
3570
 
3555
3571
  self.len(1, nodes)
3556
3572
 
3573
+ async def test_ast_subgraph_multipivot(self):
3574
+ async with self.getTestCore() as core:
3575
+ guid = s_common.guid()
3576
+ await core.nodes('''[
3577
+ (test:guid=$guid :size=1234 :tick=now) +(refs)> {[test:str=blorp]}
3578
+ ]''', opts={'vars': {'guid': guid}})
3579
+
3580
+ opts = {
3581
+ 'graph': {
3582
+ 'pivots': ('-> *', '-(refs)> *'),
3583
+ 'refs': True,
3584
+ 'degrees': 1
3585
+ }
3586
+ }
3587
+
3588
+ nodes = []
3589
+ async with await core.snap() as snap:
3590
+ async for node, path in snap.storm('test:guid', opts=opts):
3591
+ nodes.append(node)
3592
+
3593
+ opts = {
3594
+ 'graph': {
3595
+ 'pivots': ('-(refs)> *', '-> *'),
3596
+ 'refs': True,
3597
+ 'degrees': 1
3598
+ }
3599
+ }
3600
+ nodes2 = []
3601
+ async with await core.snap() as snap:
3602
+ async for node, path in snap.storm('test:guid', opts=opts):
3603
+ nodes2.append(node)
3604
+
3605
+ self.eq(set(n.iden() for n in nodes), set(n.iden() for n in nodes2))
3606
+ self.len(3, nodes)
3607
+ ndefs = [n.ndef for n in nodes]
3608
+ self.isin(('test:guid', guid), ndefs)
3609
+ self.isin(('test:str', 'blorp'), ndefs)
3610
+ self.isin(('test:int', 1234), ndefs)
3611
+
3557
3612
  async def test_ast_double_init_fini(self):
3558
3613
  async with self.getTestCore() as core:
3559
3614
  q = '''
@@ -62,3 +62,11 @@ class BossTest(s_test.SynTest):
62
62
  coro = boss.schedCoro(double_promote())
63
63
  self.true(await stream.wait(timeout=6))
64
64
  await coro
65
+
66
+ async with boss.shutdown_lock:
67
+ with self.raises(s_exc.ShuttingDown):
68
+ boss.reqNotShut()
69
+
70
+ boss.is_shutdown = True
71
+ with self.raises(s_exc.ShuttingDown):
72
+ boss.reqNotShut()
@@ -187,6 +187,20 @@ testDataSchema_v1 = {
187
187
  'size': {'type': 'number'},
188
188
  'stuff': {'type': ['number', 'null'], 'default': None},
189
189
  'woot': {'type': 'string'},
190
+ 'blorp': {
191
+ 'type': 'object',
192
+ 'properties': {
193
+ 'bleep': {
194
+ 'type': 'array',
195
+ 'items': {
196
+ 'type': 'object',
197
+ 'properties': {
198
+ 'neato': {'type': 'string'}
199
+ }
200
+ }
201
+ }
202
+ }
203
+ }
190
204
  },
191
205
  'required': ['type', 'size', 'woot'],
192
206
  'additionalProperties': False,
@@ -244,13 +258,21 @@ class CellTest(s_t_utils.SynTest):
244
258
  with self.raises(s_exc.BadVersion):
245
259
  await cell.drive.setTypeSchema('woot', testDataSchema_v0, vers=0)
246
260
 
247
- info = {'name': 'win32k.sys', 'type': 'woot'}
261
+ info = {'name': 'win32k.sys', 'type': 'woot', 'perm': {'users': {}}}
248
262
  info = await cell.addDriveItem(info, reldir=rootdir)
263
+ self.notin('perm', info)
264
+ self.eq(info[0]['permissions'], {
265
+ 'users': {},
266
+ 'roles': {}
267
+ })
249
268
 
250
269
  iden = info[-1].get('iden')
251
270
 
252
271
  tick = s_common.now()
253
272
  rootuser = cell.auth.rootuser.iden
273
+ fooser = await cell.auth.addUser('foo')
274
+ neatrole = await cell.auth.addRole('neatrole')
275
+ await fooser.grant(neatrole.iden)
254
276
 
255
277
  with self.raises(s_exc.SchemaViolation):
256
278
  versinfo = {'version': (1, 0, 0), 'updated': tick, 'updater': rootuser}
@@ -305,6 +327,10 @@ class CellTest(s_t_utils.SynTest):
305
327
  info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'haha', 'size': 17, 'stuff': 15})
306
328
  self.eq(versinfo, (await cell.getDriveData(iden))[0])
307
329
 
330
+ await cell.setDriveItemProp(iden, versinfo, ('stuff',), 1234)
331
+ data = await cell.getDriveData(iden)
332
+ self.eq(data[1]['stuff'], 1234)
333
+
308
334
  # This will be done by the cell in a cell storage version migration...
309
335
  async def migrate_v1(info, versinfo, data):
310
336
  data['woot'] = 'woot'
@@ -312,6 +338,37 @@ class CellTest(s_t_utils.SynTest):
312
338
 
313
339
  await cell.drive.setTypeSchema('woot', testDataSchema_v1, migrate_v1)
314
340
 
341
+ versinfo['version'] = (1, 1, 1)
342
+ await cell.setDriveItemProp(iden, versinfo, 'stuff', 3829)
343
+ data = await cell.getDriveData(iden)
344
+ self.eq(data[0]['version'], (1, 1, 1))
345
+ self.eq(data[1]['stuff'], 3829)
346
+
347
+ await self.asyncraises(s_exc.NoSuchIden, cell.setDriveItemProp(s_common.guid(), versinfo, ('lolnope',), 'not real'))
348
+
349
+ await self.asyncraises(s_exc.BadArg, cell.setDriveItemProp(iden, versinfo, ('blorp', 0, 'neato'), 'my special string'))
350
+ data[1]['blorp'] = {
351
+ 'bleep': [{'neato': 'thing'}]
352
+ }
353
+ info, versinfo = await cell.setDriveData(iden, versinfo, data[1])
354
+ now = s_common.now()
355
+ versinfo['updated'] = now
356
+ await cell.setDriveItemProp(iden, versinfo, ('blorp', 'bleep', 0, 'neato'), 'my special string')
357
+ data = await cell.getDriveData(iden)
358
+ self.eq(now, data[0]['updated'])
359
+ self.eq('my special string', data[1]['blorp']['bleep'][0]['neato'])
360
+
361
+ versinfo['version'] = (1, 2, 1)
362
+ await cell.delDriveItemProp(iden, versinfo, ('blorp', 'bleep', 0, 'neato'))
363
+ vers, data = await cell.getDriveData(iden)
364
+ self.eq((1, 2, 1), vers['version'])
365
+ self.nn(data['blorp']['bleep'][0])
366
+ self.notin('neato', data['blorp']['bleep'][0])
367
+
368
+ await self.asyncraises(s_exc.NoSuchIden, cell.delDriveItemProp(s_common.guid(), versinfo, 'blorp'))
369
+
370
+ self.none(await cell.delDriveItemProp(iden, versinfo, ('lolnope', 'nopath')))
371
+
315
372
  versinfo, data = await cell.getDriveData(iden, vers=(1, 0, 0))
316
373
  self.eq('woot', data.get('woot'))
317
374
 
@@ -325,10 +382,10 @@ class CellTest(s_t_utils.SynTest):
325
382
  await cell.getDriveInfo(iden, typename='newp')
326
383
 
327
384
  self.nn(await cell.getDriveInfo(iden))
328
- self.len(2, [vers async for vers in cell.getDriveDataVersions(iden)])
385
+ self.len(4, [vers async for vers in cell.getDriveDataVersions(iden)])
329
386
 
330
387
  await cell.delDriveData(iden)
331
- self.len(1, [vers async for vers in cell.getDriveDataVersions(iden)])
388
+ self.len(3, [vers async for vers in cell.getDriveDataVersions(iden)])
332
389
 
333
390
  await cell.delDriveInfo(iden)
334
391
 
@@ -352,8 +409,12 @@ class CellTest(s_t_utils.SynTest):
352
409
  baziden = pathinfo[2].get('iden')
353
410
  self.eq(pathinfo, await cell.drive.getItemPath(baziden))
354
411
 
355
- info = await cell.setDriveInfoPerm(baziden, {'users': {rootuser: 3}, 'roles': {}})
356
- self.eq(3, info['perm']['users'][rootuser])
412
+ info = await cell.setDriveInfoPerm(baziden, {'users': {rootuser: s_cell.PERM_ADMIN}, 'roles': {}})
413
+ # make sure drive perms work with easy perms
414
+ self.true(cell._hasEasyPerm(info, cell.auth.rootuser, s_cell.PERM_ADMIN))
415
+ # defaults to READ
416
+ self.true(cell._hasEasyPerm(info, fooser, s_cell.PERM_READ))
417
+ self.false(cell._hasEasyPerm(info, fooser, s_cell.PERM_EDIT))
357
418
 
358
419
  with self.raises(s_exc.NoSuchIden):
359
420
  # s_drive.rootdir is all 00s... ;)
@@ -648,6 +709,44 @@ class CellTest(s_t_utils.SynTest):
648
709
  with self.raises(s_exc.NeedConfValu):
649
710
  await echo.reqAhaProxy()
650
711
 
712
+ async def test_cell_drive_perm_migration(self):
713
+ async with self.getRegrCore('drive-perm-migr') as core:
714
+ item = await core.getDrivePath('driveitemdefaultperms')
715
+ self.len(1, item)
716
+ self.notin('perm', item)
717
+ self.eq(item[0]['permissions'], {'users': {}, 'roles': {}})
718
+
719
+ ldog = await core.auth.getRoleByName('littledog')
720
+ bdog = await core.auth.getRoleByName('bigdog')
721
+
722
+ louis = await core.auth.getUserByName('lewis')
723
+ tim = await core.auth.getUserByName('tim')
724
+ mj = await core.auth.getUserByName('mj')
725
+
726
+ item = await core.getDrivePath('permfolder/driveitemwithperms')
727
+ self.len(2, item)
728
+ self.notin('perm', item[0])
729
+ self.notin('perm', item[1])
730
+ self.eq(item[0]['permissions'], {'users': {tim.iden: s_cell.PERM_ADMIN}, 'roles': {}})
731
+ self.eq(item[1]['permissions'], {
732
+ 'users': {
733
+ mj.iden: s_cell.PERM_ADMIN
734
+ },
735
+ 'roles': {
736
+ ldog.iden: s_cell.PERM_READ,
737
+ bdog.iden: s_cell.PERM_EDIT,
738
+ },
739
+ 'default': s_cell.PERM_DENY
740
+ })
741
+
742
+ # make sure it's all good with easy perms
743
+ self.true(core._hasEasyPerm(item[0], tim, s_cell.PERM_ADMIN))
744
+ self.false(core._hasEasyPerm(item[0], mj, s_cell.PERM_EDIT))
745
+
746
+ self.true(core._hasEasyPerm(item[1], mj, s_cell.PERM_ADMIN))
747
+ self.true(core._hasEasyPerm(item[1], tim, s_cell.PERM_READ))
748
+ self.true(core._hasEasyPerm(item[1], louis, s_cell.PERM_EDIT))
749
+
651
750
  async def test_cell_unix_sock(self):
652
751
 
653
752
  async with self.getTestCore() as core:
@@ -576,6 +576,10 @@ class CertDirTest(s_t_utils.SynTest):
576
576
  key = cdir.getHostKey(hostname)
577
577
  self.basic_assertions(cdir, cert, key, cacert=cacert)
578
578
 
579
+ self.true(cdir.delHostCsr(hostname))
580
+ self.false(os.path.isfile(path))
581
+ self.false(cdir.delHostCsr(hostname))
582
+
579
583
  # Per RFC5280 common-name has a length of 1 to 64 characters
580
584
  # Do not generate CSRs which exceed that name range.
581
585
  with self.raises(s_exc.CryptoErr) as cm:
@@ -610,6 +614,10 @@ class CertDirTest(s_t_utils.SynTest):
610
614
  key = cdir.getUserKey(username)
611
615
  self.basic_assertions(cdir, cert, key, cacert=cacert)
612
616
 
617
+ self.true(cdir.delUserCsr(username))
618
+ self.false(os.path.isfile(path))
619
+ self.false(cdir.delUserCsr(username))
620
+
613
621
  # Per RFC5280 common-name has a length of 1 to 64 characters
614
622
  # Do not generate CSRs which exceed that name range.
615
623
  with self.raises(s_exc.CryptoErr) as cm:
@@ -235,3 +235,8 @@ class CoroTest(s_t_utils.SynTest):
235
235
  results = await s_coro.await_bg_tasks()
236
236
  self.len(1, results)
237
237
  self.isinstance(results[0], ZeroDivisionError)
238
+
239
+ task = s_coro.create_task(sleep(10))
240
+ self.eq([], await s_coro.await_bg_tasks(timeout=0.001))
241
+ task.cancel()
242
+ self.eq([], await s_coro.await_bg_tasks())
@@ -807,8 +807,8 @@ class HttpApiTest(s_tests.SynTest):
807
807
  opts['user'] = newpuser
808
808
  async with sess.get(f'https://localhost:{port}/api/v1/storm', json=data) as resp:
809
809
  self.eq(resp.status, http.HTTPStatus.BAD_REQUEST)
810
- data = await resp.json()
811
- self.eq(data, {'status': 'err', 'code': 'NoSuchUser',
810
+ info = await resp.json()
811
+ self.eq(info, {'status': 'err', 'code': 'NoSuchUser',
812
812
  'mesg': f'No user found with iden: {newpuser}'})
813
813
 
814
814
  async def test_http_coreinfo(self):
@@ -1615,6 +1615,14 @@ class HttpApiTest(s_tests.SynTest):
1615
1615
  json={'query': '.created', 'opts': {'user': lowuser.iden, 'view': fork}}) as resp:
1616
1616
  self.eq(resp.status, http.HTTPStatus.FORBIDDEN)
1617
1617
 
1618
+ # ShuttingDown precondition failure
1619
+ core.boss.is_shutdown = True
1620
+ async with sess.get(f'https://localhost:{port}/api/v1/storm', json={'query': '.created'}) as resp:
1621
+ self.eq(resp.status, http.HTTPStatus.BAD_REQUEST)
1622
+ info = await resp.json()
1623
+ self.eq(info.get('code'), 'ShuttingDown')
1624
+ core.boss.is_shutdown = False
1625
+
1618
1626
  # check reqvalidstorm with various queries
1619
1627
  tvs = (
1620
1628
  ('test:str=test', {}, 'ok'),
@@ -1,4 +1,5 @@
1
1
  import synapse.exc as s_exc
2
+ import synapse.lib.lmdbslab as s_lmdbslab
2
3
  import synapse.lib.jsonstor as s_jsonstor
3
4
 
4
5
  import synapse.tests.utils as s_test
@@ -125,3 +126,47 @@ class JsonStorTest(s_test.SynTest):
125
126
 
126
127
  self.true(await prox.delQueue('hehe'))
127
128
  self.false(await prox.delQueue('hehe'))
129
+
130
+ async def test_lib_jsonstor_syncing(self):
131
+
132
+ with self.getTestDir() as dirn:
133
+ async with self.getTestJsonStor(dirn=dirn) as jsonstor:
134
+ async with jsonstor.getLocalProxy() as prox:
135
+
136
+ await prox.setPathObj('foo/bar', {'hehe': 'haha'})
137
+ await s_lmdbslab.Slab.syncLoopOnce()
138
+
139
+ self.true(await prox.setPathObjProp('foo/bar', 'zip', 'zop'))
140
+ self.len(1, jsonstor.jsonstor.dirty.items())
141
+
142
+ await s_lmdbslab.Slab.syncLoopOnce()
143
+ self.len(0, jsonstor.jsonstor.dirty.items())
144
+
145
+ self.eq({'hehe': 'haha', 'zip': 'zop'}, await prox.getPathObj('foo/bar'))
146
+
147
+ self.true(await prox.delPathObjProp('foo/bar', 'zip'))
148
+ self.len(1, jsonstor.jsonstor.dirty.items())
149
+
150
+ await s_lmdbslab.Slab.syncLoopOnce()
151
+ self.len(0, jsonstor.jsonstor.dirty.items())
152
+
153
+ self.eq({'hehe': 'haha'}, await prox.getPathObj('foo/bar'))
154
+
155
+ self.true(await prox.cmpDelPathObjProp('foo/bar', 'hehe', 'haha'))
156
+ self.len(1, jsonstor.jsonstor.dirty.items())
157
+
158
+ await s_lmdbslab.Slab.syncLoopOnce()
159
+ self.len(0, jsonstor.jsonstor.dirty.items())
160
+
161
+ self.eq({}, await prox.getPathObj('foo/bar'))
162
+
163
+ self.true(await prox.setPathObjProp('foo/bar', 'zip', 'zop'))
164
+ await s_lmdbslab.Slab.syncLoopOnce()
165
+
166
+ self.true(await prox.popPathObjProp('foo/bar', 'zip'))
167
+ self.len(1, jsonstor.jsonstor.dirty.items())
168
+
169
+ await s_lmdbslab.Slab.syncLoopOnce()
170
+ self.len(0, jsonstor.jsonstor.dirty.items())
171
+
172
+ self.eq({}, await prox.getPathObj('foo/bar'))
@@ -1281,6 +1281,16 @@ class LayerTest(s_t_utils.SynTest):
1281
1281
 
1282
1282
  self.len(0, list(layr.dataslab.scanByDups(abrv, db=layr.dataname)))
1283
1283
 
1284
+ await core.addTagProp('score2', ('int', {}), {})
1285
+ nodes = await core.nodes('[test:str=multi +#foo:score=5 +#foo:score2=6]')
1286
+ self.eq(('score', 'score2'), nodes[0].getTagProps('foo'))
1287
+
1288
+ nodes = await core.nodes('test:str=multi [-#foo:score]')
1289
+ self.eq(('score2',), nodes[0].getTagProps('foo'))
1290
+
1291
+ nodes = await core.nodes('test:str=multi')
1292
+ self.eq(('score2',), nodes[0].getTagProps('foo'))
1293
+
1284
1294
  async def test_layer_waitForHot(self):
1285
1295
  self.thisHostMust(hasmemlocking=True)
1286
1296
 
@@ -121,7 +121,7 @@ class LinkTest(s_test.SynTest):
121
121
  self.eq(msg0, ('what', {'k': 1}))
122
122
  evt.set()
123
123
  await asyncio.sleep(0)
124
- with self.getAsyncLoggerStream('synapse.lib.link', 'rx error') as stream:
124
+ with self.getAsyncLoggerStream('synapse.lib.link', 'rx closed unexpectedly') as stream:
125
125
  msg1 = await link.rx()
126
126
  self.true(await stream.wait(6))
127
127
  self.none(msg1)
@@ -130,7 +130,6 @@ class StormTest(s_t_utils.SynTest):
130
130
  self.none(props.get('phone'))
131
131
  self.eq(props.get('name'), 'burrito corp')
132
132
  self.eq(props.get('desc'), 'burritos man')
133
- self.stormIsInWarn('Skipping bad value for prop ou:org:phone: requires a digit string', msgs)
134
133
 
135
134
  await self.asyncraises(s_exc.BadTypeValu, core.addNode(core.auth.rootuser, 'ou:org', {'name': 'org name 77', 'phone': 'lolnope'}, props={'desc': 'an org desc'}))
136
135
 
@@ -173,6 +172,127 @@ class StormTest(s_t_utils.SynTest):
173
172
  nodes = await core.nodes('[ it:exec:proc=(nulltime,) ]')
174
173
  self.len(1, nodes)
175
174
 
175
+ # Recursive gutors
176
+ nodes = await core.nodes('''[
177
+ inet:service:message=({
178
+ 'id': 'foomesg',
179
+ 'channel': {
180
+ 'id': 'foochannel',
181
+ 'platform': {
182
+ 'name': 'fooplatform',
183
+ 'url': 'http://foo.com'
184
+ }
185
+ }
186
+ })
187
+ ]''')
188
+ self.len(1, nodes)
189
+ node = nodes[0]
190
+ self.eq(node.ndef[0], 'inet:service:message')
191
+ self.eq(node.get('id'), 'foomesg')
192
+ self.nn(node.get('channel'))
193
+
194
+ nodes = await core.nodes('inet:service:message -> inet:service:channel')
195
+ self.len(1, nodes)
196
+ node = nodes[0]
197
+ self.eq(node.get('id'), 'foochannel')
198
+ self.nn(node.get('platform'))
199
+
200
+ nodes = await core.nodes('inet:service:message -> inet:service:channel -> inet:service:platform')
201
+ self.len(1, nodes)
202
+ node = nodes[0]
203
+ self.eq(node.get('name'), 'fooplatform')
204
+ self.eq(node.get('url'), 'http://foo.com')
205
+
206
+ nodes = await core.nodes('''
207
+ inet:service:message=({
208
+ 'id': 'foomesg',
209
+ 'channel': {
210
+ 'id': 'foochannel',
211
+ 'platform': {
212
+ 'name': 'fooplatform',
213
+ 'url': 'http://foo.com'
214
+ }
215
+ }
216
+ })
217
+ ''')
218
+ self.len(1, nodes)
219
+ node = nodes[0]
220
+ self.eq(node.ndef[0], 'inet:service:message')
221
+ self.eq(node.get('id'), 'foomesg')
222
+
223
+ nodes = await core.nodes('''[
224
+ inet:service:message=({
225
+ 'id': 'barmesg',
226
+ 'channel': {
227
+ 'id': 'barchannel',
228
+ 'platform': {
229
+ 'name': 'barplatform',
230
+ 'url': 'http://bar.com'
231
+ }
232
+ },
233
+ '$props': {
234
+ 'platform': {
235
+ 'name': 'barplatform',
236
+ 'url': 'http://bar.com'
237
+ }
238
+ }
239
+ })
240
+ ]''')
241
+ self.len(1, nodes)
242
+ node = nodes[0]
243
+ self.eq(node.ndef[0], 'inet:service:message')
244
+ self.eq(node.get('id'), 'barmesg')
245
+ self.nn(node.get('channel'))
246
+
247
+ platguid = node.get('platform')
248
+ self.nn(platguid)
249
+ nodes = await core.nodes('inet:service:message:id=barmesg -> inet:service:channel -> inet:service:platform')
250
+ self.len(1, nodes)
251
+ self.eq(platguid, nodes[0].ndef[1])
252
+
253
+ # No node lifted if no matching node for inner gutor
254
+ self.len(0, await core.nodes('''
255
+ inet:service:message=({
256
+ 'id': 'foomesg',
257
+ 'channel': {
258
+ 'id': 'foochannel',
259
+ 'platform': {
260
+ 'name': 'newp',
261
+ 'url': 'http://foo.com'
262
+ }
263
+ }
264
+ })
265
+ '''))
266
+
267
+ # BadTypeValu comes through from inner gutor
268
+ with self.raises(s_exc.BadTypeValu) as cm:
269
+ await core.nodes('''
270
+ inet:service:message=({
271
+ 'id': 'foomesg',
272
+ 'channel': {
273
+ 'id': 'foochannel',
274
+ 'platform': {
275
+ 'name': 'newp',
276
+ 'url': 'newp'
277
+ }
278
+ }
279
+ })
280
+ ''')
281
+
282
+ self.eq(cm.exception.get('form'), 'inet:service:platform')
283
+ self.eq(cm.exception.get('prop'), 'url')
284
+ self.eq(cm.exception.get('mesg'), 'Bad value for prop inet:service:platform:url: Invalid/Missing protocol')
285
+
286
+ # Ensure inner nodes are not created unless the entire gutor is valid.
287
+ self.len(0, await core.nodes('''[
288
+ inet:service:account?=({
289
+ "id": "bar",
290
+ "platform": {"name": "barplat"},
291
+ "url": "newp"})
292
+ ]'''))
293
+
294
+ self.len(0, await core.nodes('inet:service:platform:name=barplat'))
295
+
176
296
  async def test_lib_storm_jsonexpr(self):
177
297
  async with self.getTestCore() as core:
178
298
 
@@ -94,7 +94,7 @@ class StormLibItersTest(s_test.SynTest):
94
94
  self.stormIsInErr(err, msgs)
95
95
 
96
96
  async def boom(self, genr):
97
- print(newp)
97
+ raise NameError("name 'newp' is not defined")
98
98
  yield True
99
99
 
100
100
  with mock.patch.object(s_stormlib_iters.LibIters, 'enum', boom):
@@ -1,3 +1,4 @@
1
+ import os
1
2
  import synapse.exc as s_exc
2
3
  import synapse.lib.stormtypes as s_stormtypes
3
4
 
@@ -144,6 +145,20 @@ class StormlibSpooledTest(s_test.SynTest):
144
145
  valu = await core.callStorm(q)
145
146
  self.eq(1500, valu)
146
147
 
148
+ subq = '''{
149
+ $sset = $lib.spooled.set()
150
+ $sset.adds($lib.range(1500))
151
+ return($sset)
152
+ }'''
153
+
154
+ q = '''
155
+ $subset = $lib.storm.eval($text)
156
+ $subset.add(2345)
157
+ return($subset.size())
158
+ '''
159
+ valu = await core.callStorm(q, opts={'vars': {'text': subq}})
160
+ self.eq(1501, valu)
161
+
147
162
  # sad paths
148
163
  # too complex
149
164
  q = '''
@@ -199,3 +214,8 @@ class StormlibSpooledTest(s_test.SynTest):
199
214
  return($set)
200
215
  '''
201
216
  await self.asyncraises(s_exc.StormRuntimeError, core.callStorm(q, {'vars': {'stormnode': stormnode}}))
217
+
218
+ # make sure the various spools got trashed
219
+ path = os.path.join(core.dirn, 'tmp')
220
+ files = [f for f in os.listdir(os.path.join(path))]
221
+ self.len(0, files)