tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

practical.py (6631B)


      1 """Portable demo presets and independent expected records for prepare.py."""
      2 def literal(value):
      3     if isinstance(value, bool): return dict(type='bool', value=value)
      4     if isinstance(value, int): return dict(type='number', value=str(value))
      5     if isinstance(value, str): return dict(type='string', value=value)
      6     return dict(type='list', value=[literal(v) for v in value])
      7 
      8 
      9 def settings():
     10     defaults = {
     11         'shipping': ('Shipping policies', 'v1', ['domestic', 8000, 1500], ['v1', 'v2']),
     12         'events': ('Event adapters', 'vendorA', [['purchase', 'James', 2499]], ['vendorA', 'vendorB']),
     13         'registration': ('Registration validator', 'main', ['James', 'james@example.org', 21, True], ['main']),
     14         'migration': ('Profile migrations', 'v1', [['James', 'james@example.org']], ['v1', 'v2']),
     15     }
     16     result = {}
     17     for name, (title, export, args, exports) in defaults.items():
     18         presets = {e: dict(arguments=[literal(v) for v in args], output='record', mode='pure') for e in exports}
     19         if name == 'events': presets['vendorB']['arguments'] = [literal(['James', False, 2499])]
     20         result[name] = dict(title=title, exports=exports, export=export, args=[literal(v) for v in args],
     21                             output='record', mode='pure', presets=presets)
     22     return result
     23 
     24 
     25 def cases():
     26     result = {name: [] for name in settings()}
     27     def add(name, export, args, expected):
     28         result[name].append((export, [literal(v) if not isinstance(v, dict) else v for v in args], expected))
     29     def error(message): return dict(status='error', message=message)
     30     raw = lambda tree: dict(type='tree', value=tree)
     31     for version in [1, 2]:
     32         for zone, subtotal, grams in [('domestic', 8000, 1500), ('domestic', 7499, 1000),
     33               ('domestic', 7500, 1001), ('domestic', 10000, 5000), ('regional', 0, 1000), ('international', 0, 1001)]:
     34             free = zone == 'domestic' and subtotal >= (7500 if version == 1 else 10000)
     35             base = (499 if version == 1 else 599) if zone == 'domestic' else (999 if zone == 'regional' else 1999)
     36             cents = 0 if free else base + (250 if grams > 1000 else 0)
     37             add('shipping', f'v{version}', [zone, subtotal, grams], dict(status='ok', policyVersion=version, destination=zone, shippingCents=cents))
     38     for args, message in [(['moon', 0, 10], 'Unknown destination.'),
     39        (['domestic', 0, 0], 'Subtotal must be <=1000000 cents; grams must be 1..5000.'),
     40        (['domestic', 0, 5001], 'Subtotal must be <=1000000 cents; grams must be 1..5000.'),
     41        (['domestic', 1000001, 1], 'Subtotal must be <=1000000 cents; grams must be 1..5000.'),
     42        (['domestic', raw('10'), 1], 'Subtotal and grams must be canonical natural numbers.'),
     43        (['domestic', 0, raw('21100')], 'Subtotal and grams must be canonical natural numbers.'),
     44        (['domestic', raw('200'), 1], 'Subtotal and grams must be canonical natural numbers.')]:
     45         add('shipping', 'v1', args, error(message))
     46     for customer in ['James', 'λ雪🙂', '<img src=x onerror=alert(1)>']:
     47         for refunded in [False, True]:
     48             expected = dict(schemaVersion=1, eventType='order.refunded' if refunded else 'order.created', customer=customer, amountCents=2499)
     49             add('events', 'vendorA', [[('refund' if refunded else 'purchase'), customer, 2499]], expected)
     50             add('events', 'vendorB', [[customer, refunded, 2499]], expected)
     51     for export, args, message in [('vendorA', [['other', 'James', 0]], 'Unknown vendor A event.'),
     52         ('vendorA', [['purchase', 'James', 1000001]], 'amountCents must be a canonical natural <=1000000.'),
     53         ('vendorA', [[1, 'James', 0]], 'Unknown vendor A event.'),
     54         ('vendorA', [['purchase', 1, 0]], 'customer must be scalar text.'),
     55         ('vendorA', [[]], 'Expected exactly three fields.'),
     56         ('vendorA', [['purchase','James',0,'extra']], 'Expected exactly three fields.'),
     57         ('vendorB', [['James', 2, 0]], 'refunded must be a canonical Boolean.'),
     58         ('vendorB', [raw('10')], 'Expected a list.')]:
     59         add('events', export, args, error(message))
     60     def report(name='', email='', age='', terms=''):
     61         return dict(accepted=not any([name,email,age,terms]), nameError=name, emailError=email, ageError=age, termsError=terms)
     62     for age in [18, 21, 120]: add('registration','main',['James','james@example.org',age,True], report())
     63     add('registration','main',['','bad',17,False],report('Name is required.','Email must contain @ and a dot.','Age must be from 18 to 120.','Accept the terms.'))
     64     add('registration','main',['λ雪🙂','a@b.c',121,True],report(age='Age must be from 18 to 120.'))
     65     add('registration','main',[1,2,raw('21100'),3],report('Name must be scalar text.','Email must be scalar text.','Age must be a canonical natural.','Accept the terms.'))
     66     for version in [1,2]:
     67         for name,email in [('James','james@example.org'), ('λ雪🙂',''), ('<script>','a"b@example.org')]:
     68             expected = dict(schemaVersion=version,displayName=name,email=email)
     69             if version==2: expected['marketingOptIn']=False
     70             add('migration',f'v{version}',[[name,email]],expected)
     71         for args,message in [([[]],'Expected [name email].'), ([['James']],'Expected [name email].'),
     72                   ([['James','email','extra']],'Expected [name email].'), ([[1,'email']],'Name and email must be scalar text.'),
     73                   ([raw('10')],'Expected a list.')]: add('migration',f'v{version}',args,error(message))
     74     # Malformed spines keep precise diagnostics; scalar and numeric boundaries
     75     # are expectations, not outputs borrowed from the historical implementation.
     76     for export in ['vendorA', 'vendorB']:
     77         for tree, message in [('2010', 'Expected a list.'), ('202010', 'Expected a list.'),
     78                               ('20202010', 'Expected exactly three fields.')]:
     79             add('events', export, [raw(tree)], error(message))
     80     for version in [1, 2]:
     81         add('migration', f'v{version}', [raw('2010')], error('Expected a list.'))
     82         add('migration', f'v{version}', [raw('202010')], error('Expected [name email].'))
     83         add('migration', f'v{version}', [[[55296], '']], error('Name and email must be scalar text.'))
     84         add('migration', f'v{version}', [[[1114112], '']], error('Name and email must be scalar text.'))
     85     add('shipping', 'v1', ['moon', raw('200'), 0], error('Subtotal and grams must be canonical natural numbers.'))
     86     add('events', 'vendorA', [['purchase', 'James', raw('200')]], error('amountCents must be a canonical natural <=1000000.'))
     87     return result