summaryrefslogtreecommitdiff
path: root/yarns
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2017-11-13 11:43:37 +0100
committerLars Wirzenius <liw@liw.fi>2017-11-14 12:06:33 +0100
commit5bca14e72c204b31424069ad0090aefa76071e8c (patch)
treece00b14ed66436d7175ce04b0ce48f1b89e97c3f /yarns
parent607f62ae6bc8f684095e447569cff27ba7c8a5dd (diff)
downloadqvisqve-5bca14e72c204b31424069ad0090aefa76071e8c.tar.gz
Fix: start Salami
This is based on Qvarn, because the implementation is so similar to hat Salami needs. This commit drops the unwanted bits of Qvarn and changes things to be Salami instead. This commit only introduces the /version endpoint.
Diffstat (limited to 'yarns')
-rw-r--r--yarns/900-implements.yarn272
-rw-r--r--yarns/lib.py157
-rw-r--r--yarns/smoke.yarn1204
3 files changed, 0 insertions, 1633 deletions
diff --git a/yarns/900-implements.yarn b/yarns/900-implements.yarn
deleted file mode 100644
index 2f7ee0f..0000000
--- a/yarns/900-implements.yarn
+++ /dev/null
@@ -1,272 +0,0 @@
-# Step implementations
-
-This chapter shows the scenario step implementations.
-
-## Start and stop Qvarn
-
-Start a Qvarn running in the background.
-
- IMPLEMENTS GIVEN a running qvarn instance
- import os, time, cliapp, yaml, yarnutils
- privkey, pubkey = create_token_signing_key_pair()
- print repr(privkey)
- print repr(pubkey)
- assert privkey
- assert pubkey
- open('key', 'w').write(privkey)
- vars['aud'] = 'http://api.test.example.com'
- vars['iss'] = 'qvarn.yarn'
- vars['privkey'] = privkey
- vars['pubkey'] = pubkey
- vars['api.log'] = 'qvarn.log'
- vars['gunicorn3.log'] = 'gunicorn3.log'
- vars['pid-file'] = 'pid'
- vars['port'] = cliapp.runcmd([os.path.join(srcdir, 'randport' )]).strip()
- vars['url'] = 'http://127.0.0.1:{}'.format(vars['port'])
- vars['API_URL'] = vars['url']
- config = {
- 'log': [
- {
- 'filename': vars['api.log'],
- },
- ],
- 'baseurl': vars['url'],
- 'token-issuer': vars['iss'],
- 'token-audience': vars['aud'],
- 'token-public-key': vars['pubkey'],
- 'resource-type-dir': os.path.join(srcdir, 'resource_type'),
- }
- config = add_postgres_config(config)
- env = dict(os.environ)
- env['QVARN_CONFIG'] = os.path.join(datadir, 'qvarn.yaml')
- yaml.safe_dump(config, open(env['QVARN_CONFIG'], 'w'))
- argv = [
- 'gunicorn3',
- '--daemon',
- '--bind', '127.0.0.1:{}'.format(vars['port']),
- '-p', vars['pid-file'],
- 'qvarn.backend:app',
- ]
- cliapp.runcmd(argv, env=env, stdout=None, stderr=None)
- until = time.time() + 2.0
- while time.time() < until and not os.path.exists(vars['pid-file']):
- time.sleep(0.01)
- assert os.path.exists(vars['pid-file'])
-
-## Stop a Qvarn we started
-
- IMPLEMENTS FINALLY qvarn is stopped
- import os, signal, yarnutils
- pid = int(cat(vars['pid-file']))
- os.kill(pid, signal.SIGTERM)
-
-## API requests of various kinds
-
- IMPLEMENTS WHEN client requests GET (/.+) without token
- path = get_next_match()
- path = expand_vars(path, vars)
- vars['status_code'], vars['headers'], vars['body'] = get(vars['url'] + path)
-
- IMPLEMENTS WHEN client requests GET (/.+) using token
- path = get_next_match()
- path = expand_vars(path, vars)
- headers = {
- 'Authorization': 'Bearer {}'.format(vars['token']),
- }
- vars['status_code'], vars['headers'], vars['body'] = get(
- vars['url'] + path, headers)
-
- IMPLEMENTS WHEN client requests POST (/.+) with token and body (.+)
- path = get_next_match()
- body = get_next_match()
- headers = {
- 'Authorization': 'Bearer {}'.format(vars['token']),
- 'Content-Type': 'application/json',
- }
- vars['status_code'], vars['headers'], vars['body'] = post(
- vars['url'] + path, headers=headers, body=body)
-
- IMPLEMENTS WHEN client requests PUT (/.+) with token and body (.+)
- path = get_next_match()
- path = expand_vars(path, vars)
- body = get_next_match()
- body = expand_vars(body, vars)
- headers = {
- 'Authorization': 'Bearer {}'.format(vars['token']),
- 'Content-Type': 'application/json',
- }
- vars['status_code'], vars['headers'], vars['body'] = put(
- vars['url'] + path, headers=headers, body=body)
-
- IMPLEMENTS WHEN client requests PUT (/[a-z0-9/${}]+) with token, revision (\S+), content-type (\S+), and empty body
- path = expand_vars(get_next_match(), vars)
- revision = expand_vars(get_next_match(), vars)
- ctype = expand_vars(get_next_match(), vars)
- body = ''
- headers = {
- 'Authorization': 'Bearer {}'.format(vars['token']),
- 'Revision': revision,
- 'Content-Type': ctype,
- }
- vars['status_code'], vars['headers'], vars['body'] = put(
- vars['url'] + path, headers=headers, body=body)
-
- IMPLEMENTS WHEN client requests PUT (/[a-z0-9/${}]+) with token, revision (\S+), content-type (\S+), and body "(.+)"
- path = expand_vars(get_next_match(), vars)
- revision = expand_vars(get_next_match(), vars)
- ctype = expand_vars(get_next_match(), vars)
- body = unescape(expand_vars(get_next_match(), vars))
- headers = {
- 'Authorization': 'Bearer {}'.format(vars['token']),
- 'Revision': revision,
- 'Content-Type': ctype,
- }
- vars['status_code'], vars['headers'], vars['body'] = put(
- vars['url'] + path, headers=headers, body=body)
-
- IMPLEMENTS WHEN client requests DELETE (/.+) with token
- path = get_next_match()
- path = expand_vars(path, vars)
- headers = {
- 'Authorization': 'Bearer {}'.format(vars['token']),
- }
- vars['status_code'], vars['headers'], vars['body'] = delete(
- vars['url'] + path, headers=headers)
-
- IMPLEMENTS WHEN client uploads a fake jpg
- assert 0
-
-## API access token creation
-
- IMPLEMENTS WHEN client gets an authorization token with scope "(.+)"
- scopes = get_next_match()
- print 'privkey', repr(vars['privkey'])
- assert vars['privkey']
- vars['token'] = create_token(vars['privkey'], vars['iss'], vars['aud'], scopes)
-
-## UUID creation
-
- IMPLEMENTS GIVEN unique random identifier (\S+)
- import uuid
- name = get_next_match()
- vars[name] = str(uuid.uuid4())
-
-## API request result checking
-
- IMPLEMENTS THEN HTTP status code is (\d+) (.*)
- expected = int(get_next_match())
- assertEqual(vars['status_code'], expected)
-
- IMPLEMENTS THEN HTTP (\S+) header is (.+)
- header = get_next_match()
- value = expand_vars(get_next_match(), vars)
- assertEqual(vars['headers'].get(header), value)
-
- IMPLEMENTS THEN remember HTTP (\S+) header as (.+)
- header = get_next_match()
- name = get_next_match()
- vars[name] = vars['headers'].get(header)
-
- IMPLEMENTS THEN resource id is (\S+)
- import json
- name = get_next_match()
- print 'body:', repr(vars['body'])
- body = json.loads(vars['body'])
- vars[name] = body['id']
-
- IMPLEMENTS THEN revision is (\S+)
- import json
- name = get_next_match()
- body = json.loads(vars['body'])
- vars[name] = body['revision']
-
- IMPLEMENTS THEN revisions (\S+) and (\S+) are different
- rev1 = get_next_match()
- rev2 = get_next_match()
- assertNotEqual(vars[rev1], vars[rev2])
-
- IMPLEMENTS THEN revisions (\S+) and (\S+) match
- rev1 = get_next_match()
- rev2 = get_next_match()
- assertEqual(vars[rev1], vars[rev2])
-
- IMPLEMENTS THEN JSON body matches (.+)
- import json
- wanted = get_next_match()
- print 'wanted1', repr(wanted)
- wanted = expand_vars(wanted, vars)
- print 'wanted2', repr(wanted)
- wanted = json.loads(wanted)
- actual = json.loads(vars['body'])
- print 'actual ', repr(actual)
- print 'wanted3', repr(wanted)
- assertTrue(values_match(wanted, actual))
-
- IMPLEMENTS THEN body is "(.+)"
- wanted = unescape(expand_vars(get_next_match(), vars))
- body = vars['body']
- assertTrue(values_match(wanted, body))
-
- IMPLEMENTS THEN search result contains (.+)
- import json
- wanted1 = get_next_match()
- wanted2 = expand_vars(wanted1, vars)
- wanted = json.loads(wanted2)
- actual = json.loads(vars['body'])
- print 'wanted1:', repr(wanted1)
- print 'wanted2:', repr(wanted2)
- print 'wanted:', repr(wanted)
- print 'actual:', repr(actual)
- assertTrue(actual['resources'])
- found = False
- for result in actual['resources']:
- if values_match(wanted, result):
- print 'MATCH!', repr(wanted), repr(result)
- found = True
- break
- print 'no match', repr(wanted), repr(result)
- assertTrue(found)
-
- IMPLEMENTS THEN search result does NOT contain (.+)
- import json
- wanted1 = get_next_match()
- wanted2 = expand_vars(wanted1, vars)
- wanted = json.loads(wanted2)
- actual = json.loads(vars['body'])
- print 'wanted1:', repr(wanted1)
- print 'wanted2:', repr(wanted2)
- print 'wanted:', repr(wanted)
- print 'actual:', repr(actual)
- found = False
- for result in actual['resources']:
- if values_match(wanted, result):
- found = True
- assertFalse(found)
-
- IMPLEMENTS THEN search result at index (\d+) has id (\S+)
- import json
- index = int(get_next_match())
- id_name = get_next_match()
- body = json.loads(vars['body'])
- print 'body', repr(body)
- resources = body['resources']
- print 'resources', repr(resources)
- print 'len resources', len(resources)
- print 'index', index
- assert index < len(resources)
- obj = resources[index]
- print 'resource at index', repr(obj)
- print 'id', repr(obj['id'])
- vars[id_name] = obj['id']
-
- IMPLEMENTS THEN search result has (\d+) resources
- wanted = int(get_next_match())
- body = json.loads(vars['body'])
- print 'body', repr(body)
- resources = body['resources']
- print 'resources', repr(resources)
- print 'len resources', len(resources)
- assertEqual(wanted, len(resources))
-
- IMPLEMENTS THEN response has header WWW-Authenticate containing "(.+)"
- assert 0
diff --git a/yarns/lib.py b/yarns/lib.py
deleted file mode 100644
index 459a821..0000000
--- a/yarns/lib.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# Copyright (C) 2017 Lars Wirzenius
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
-import json
-import os
-import re
-import tempfile
-
-
-import cliapp
-import Crypto.PublicKey.RSA
-import requests
-import yaml
-
-
-from yarnutils import *
-
-
-srcdir = os.environ['SRCDIR']
-datadir = os.environ['DATADIR']
-
-
-vars = Variables(datadir)
-
-
-def hexdigit(c):
- return ord(c) - ord('0')
-
-
-def unescape(s):
- t = ''
- while s:
- if s.startswith('\\x') and len(s) >= 4:
- a = hexdigit(s[2])
- b = hexdigit(s[3])
- t += chr(a * 16 + b)
- s = s[4:]
- else:
- t += s[0]
- s = s[1:]
- return t
-
-
-def add_postgres_config(config):
- pg = os.environ.get('QVARN_POSTGRES')
- if pg:
- with open(pg) as f:
- config['database'] = yaml.safe_load(f)
- config['memory-database'] = False
- return config
-
-
-def get(url, headers=None):
- print('get: url={} headers={}'.format(url, headers))
- r = requests.get(url, headers=headers)
- return r.status_code, dict(r.headers), r.content
-
-
-def post(url, headers=None, body=None):
- r = requests.post(url, headers=headers, data=body)
- return r.status_code, dict(r.headers), r.text
-
-
-def put(url, headers=None, body=None):
- r = requests.put(url, headers=headers, data=body)
- return r.status_code, dict(r.headers), r.text
-
-
-def delete(url, headers=None):
- r = requests.delete(url, headers=headers)
- return r.status_code, dict(r.headers), r.text
-
-
-def create_token_signing_key_pair():
- RSA_KEY_BITS = 4096 # A nice, currently safe length
- key = Crypto.PublicKey.RSA.generate(RSA_KEY_BITS)
- return key.exportKey('PEM'), key.exportKey('OpenSSH')
-
-
-def create_token(privkey, iss, aud, scopes):
- filename = write_temp(privkey)
- argv = [
- os.path.join(srcdir, 'create-token'),
- filename,
- iss,
- aud,
- scopes,
- ]
- return cliapp.runcmd(argv)
-
-
-def cat(filename):
- return open(filename).read()
-
-
-def write_temp(data):
- fd, filename = tempfile.mkstemp(dir=datadir)
- os.write(fd, data)
- os.close(fd)
- return filename
-
-
-def expand_vars(text, vars):
- result = ''
- while text:
- m = re.search(r'\${(?P<name>[^}]+)}', text)
- if not m:
- result += text
- break
- name = m.group('name')
- print('expanding ', name)
- result += text[:m.start()] + vars[name]
- text = text[m.end():]
- return result
-
-
-def values_match(wanted, actual):
- print
- print 'wanted:', repr(wanted)
- print 'actual:', repr(actual)
-
- if type(wanted) != type(actual):
- print 'wanted and actual types differ', type(wanted), type(actual)
- return False
-
- if isinstance(wanted, dict):
- for key in wanted:
- if key not in actual:
- print 'key {!r} not in actual'.format(key)
- return False
- if not values_match(wanted[key], actual[key]):
- return False
- elif isinstance(wanted, list):
- if len(wanted) != len(actual):
- print 'wanted and actual are of different lengths'
- for witem, aitem in zip(wanted, actual):
- if not values_match(witem, aitem):
- return False
- else:
- if wanted != actual:
- print 'wanted and actual differ'
- return False
-
- return True
diff --git a/yarns/smoke.yarn b/yarns/smoke.yarn
deleted file mode 100644
index 6e774d2..0000000
--- a/yarns/smoke.yarn
+++ /dev/null
@@ -1,1204 +0,0 @@
----
-title: qvarn-jsonb integration tests
-author: Lars Wirzenius / QvarnLabs Ab
-date: work in progress
-...
-
-
-# Introduction
-
-This is an integration test suite for the Qvarn HTTP API.
-
-## History and background
-
-Qvarn started as an internal project in 2015 at Suomen Tilaajavastuu
-(https://www.tilaajavastuu.fi/en/), a company that provides various
-reproting services to the Finnish construction industry. Later that
-year the Swedish Construction Federation
-(https://www.sverigesbyggindustrier.se/english) adopted Qvarn as a
-platform for ID06 identity card services in Sweden. From the start, a
-goal for Qvarn has been to make it easy to comply with the EU General
-Data Protection Regulation.
-
-In 2016 Qvarn spun off to its own company, QvarnLabs Ab, which
-provides consulting, development, and support services around Qvarn.
-
-
-# Version checking
-
- SCENARIO Qvarn reports its version
-
- GIVEN a running qvarn instance
-
- WHEN client requests GET /version without token
- THEN HTTP status code is 200 OK
-
- WHEN client gets an authorization token with scope "uapi_version_get"
- AND client requests GET /version using token
- THEN HTTP status code is 200 OK
-
- FINALLY qvarn is stopped
-
-
-# Manage a subject
-
- SCENARIO user manages a subject resource
-
- GIVEN a running Qvarn instance
-
- WHEN client requests GET /subjects without token
- THEN HTTP status code is 401 Unauthorized
-
- WHEN client requests GET /subjects/notexist without token
- THEN HTTP status code is 401 Unauthorized
-
- WHEN client gets an authorization token with scope
- ... "uapi_subjects_get uapi_subjects_post uapi_subjects_id_get
- ... uapi_subjects_id_put uapi_subjects_id_delete"
- AND client requests GET /subjects using token
- THEN HTTP status code is 200 OK
-
- WHEN client requests GET /subjects using token
- THEN HTTP status code is 200 OK
- AND search result does NOT contain { "id": "subject" }
-
- WHEN client requests POST /subjects with token and body
- ... {
- ... "type": "subject",
- ... "names": [
- ... { "full_name": "Jason Bourne" }
- ... ]
- ... }
- THEN HTTP status code is 201 Created
- AND resource id is ID1
- AND revision is REV1
-
- WHEN client requests GET /subjects/${ID1} without token
- THEN HTTP status code is 401 Unauthorized
-
- WHEN client requests GET /subjects/${ID1} using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... { "revision": "${REV1}", "id": "${ID1}", "type": "subject",
- ... "names": [{ "full_name": "Jason Bourne" }] }
-
-For silly hysterical raisins, the Qvarn API is defined to return 400
-if updating a resource with the wrong revision in the body, instead
-of 404. We may want to fix this some day.
-
- WHEN client requests PUT /subjects/${ID1} with token and body
- ... { "id": "wrong", "revision": "${REV1}", "names": [{"full_name": "Dave Webb"}]}
- THEN HTTP status code is 400 Bad
-
- WHEN client requests PUT /subjects/${ID1} with token and body
- ... { "id": "${ID1}", "revision": "${REV1}", "names": [{"full_name": "Dave Webb"}]}
- THEN HTTP status code is 200 OK
- AND revision is REV2
-
- WHEN client requests PUT /subjects/${ID1} with token and body
- ... { "type": "subject", "id": "${ID1}", "revision": "${REV2}",
- ... "names": [{"full_name": "David Webb" }]}
- THEN HTTP status code is 200 OK
- AND revision is REV3
-
- WHEN client requests GET /subjects/${ID1} using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... { "revision": "${REV3}", "id": "${ID1}", "type": "subject",
- ... "names": [{"full_name": "David Webb" }]}
-
- WHEN client requests DELETE /subjects/${ID1} with token
- THEN HTTP status code is 200 OK
-
- WHEN client requests GET /subjects/${ID1} using token
- THEN HTTP status code is 404 Not Found
-
- FINALLY qvarn is stopped
-
-
-# Manage notifications
-
-Notifications are a special resource type. Notifications created
-automatically by Qvarn, but the API client may delete them.
-
- SCENARIO manage notifications
-
- GIVEN a running Qvarn instance
-
-Client has needed access rights for orgs resource.
-
- WHEN client gets an authorization token with scope
- ... "uapi_orgs_listeners_post uapi_orgs_listeners_id_get
- ... uapi_orgs_listeners_get uapi_orgs_listeners_id_notifications_get
- ... uapi_orgs_post uapi_orgs_listeners_id_notifications_id_get
- ... uapi_orgs_listeners_id_put uapi_orgs_id_put uapi_orgs_id_delete
- ... uapi_orgs_listeners_id_delete
- ... uapi_orgs_listeners_id_notifications_id_delete"
-
- WHEN client requests POST /orgs/listeners with token and body
- ... {
- ... "notify_of_new": true
- ... }
- THEN HTTP status code is 201 Created
- AND JSON body matches
- ... {
- ... "type": "listener",
- ... "notify_of_new": true,
- ... "listen_on": []
- ... }
- AND resource id is LISTENID1
- AND HTTP Location header is ${API_URL}/orgs/listeners/${LISTENID1}
-
- WHEN client requests POST /orgs/listeners with token and body
- ... {
- ... "notify_of_new": false
- ... }
- THEN HTTP status code is 201 Created
- AND JSON body matches
- ... {
- ... "type": "listener",
- ... "notify_of_new": false,
- ... "listen_on": []
- ... }
- AND resource id is LISTENID2
- AND HTTP Location header is ${API_URL}/orgs/listeners/${LISTENID2}
- AND revision is REV1
-
- WHEN client requests POST /orgs/listeners with token and body
- ... {
- ... "notify_of_new": false,
- ... "listen_on_all": true
- ... }
- THEN HTTP status code is 201 Created
- AND JSON body matches
- ... {
- ... "type": "listener",
- ... "notify_of_new": false,
- ... "listen_on_all": true,
- ... "listen_on": []
- ... }
- AND resource id is LISTENID3
- AND HTTP Location header is ${API_URL}/orgs/listeners/${LISTENID3}
-
- WHEN client requests GET /orgs/listeners/${LISTENID1} using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... {
- ... "id": "${LISTENID1}",
- ... "type": "listener",
- ... "notify_of_new": true,
- ... "listen_on": []
- ... }
-
- WHEN client requests GET /orgs/listeners using token
- THEN HTTP status code is 200 OK
- THEN search result contains {"id": "${LISTENID1}"}
- THEN search result contains {"id": "${LISTENID2}"}
-
-A listener has no notifications initially.
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID1}/notifications
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... {
- ... "resources": []
- ... }
-
- WHEN client requests POST /orgs with token and body
- ... {
- ... "names": ["Universal Exports"]
- ... }
- THEN resource id is ORGID1
- AND revision is REV2
-
- WHEN client requests POST /orgs with token and body
- ... {
- ... "names": ["Telebulvania Ltd"]
- ... }
- THEN resource id is ORGID2
-
-After adding the new organizations the first listener should be notified while
-the second and third should have no notifications.
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID1}/notifications
- ... using token
- THEN HTTP status code is 200 OK
- AND search result at index 0 has id MSGID1
- AND search result at index 1 has id MSGID2
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID1}/notifications/${MSGID1}
- ... using token
- THEN JSON body matches
- ... {
- ... "id": "${MSGID1}",
- ... "type": "notification",
- ... "resource_id": "${ORGID1}",
- ... "resource_change": "created"
- ... }
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID1}/notifications/${MSGID2}
- ... using token
- THEN JSON body matches
- ... {
- ... "id": "${MSGID2}",
- ... "type": "notification",
- ... "resource_id": "${ORGID2}",
- ... "resource_change": "created"
- ... }
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID2}/notifications
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... {
- ... "resources": []
- ... }
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID3}/notifications
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... {
- ... "resources": []
- ... }
-
-We update the empty listener to listen on organization changes and update the
-organization checking for the correct notification to appear. The third
-listener listening to all the changes should get the notification, too.
-
- WHEN client requests PUT /orgs/listeners/${LISTENID2} with token and body
- ... {
- ... "notify_of_new": false,
- ... "listen_on": ["${ORGID1}"],
- ... "revision": "${REV1}"
- ... }
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... {
- ... "type": "listener",
- ... "notify_of_new": false,
- ... "listen_on": ["${ORGID1}"]
- ... }
-
- WHEN client requests PUT /orgs/${ORGID1} with token and body
- ... {
- ... "names": ["Universal Experts"],
- ... "revision": "${REV2}"
- ... }
- THEN HTTP status code is 200 OK
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID2}/notifications
- ... using token
- THEN HTTP status code is 200 OK
- AND search result at index 0 has id MSGID3
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID2}/notifications/${MSGID3}
- ... using token
- THEN JSON body matches
- ... {
- ... "id": "${MSGID3}",
- ... "type": "notification",
- ... "resource_id": "${ORGID1}",
- ... "resource_change": "updated"
- ... }
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID3}/notifications
- ... using token
- THEN HTTP status code is 200 OK
- AND search result has 1 resources
- AND search result at index 0 has id MSGID4
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID3}/notifications/${MSGID4}
- ... using token
- THEN JSON body matches
- ... {
- ... "id": "${MSGID4}",
- ... "type": "notification",
- ... "resource_id": "${ORGID1}",
- ... "resource_change": "updated"
- ... }
-
-The first listener gets no additional notifications.
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID1}/notifications
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... {
- ... "resources": [
- ... {"id": "${MSGID1}"}, {"id": "${MSGID2}"}
- ... ]
- ... }
-
-We delete the organization and check for the correct notification to appear.
-
- WHEN client requests DELETE /orgs/${ORGID1} with token
- THEN HTTP status code is 200 OK
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID2}/notifications
- ... using token
- THEN HTTP status code is 200 OK
- AND search result at index 1 has id MSGID5
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID2}/notifications/${MSGID5}
- ... using token
- THEN JSON body matches
- ... {
- ... "id": "${MSGID5}",
- ... "type": "notification",
- ... "resource_id": "${ORGID1}",
- ... "resource_revision": null,
- ... "resource_change": "deleted"
- ... }
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID3}/notifications
- ... using token
- THEN HTTP status code is 200 OK
- AND search result at index 1 has id MSGID6
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID3}/notifications/${MSGID6}
- ... using token
- THEN JSON body matches
- ... {
- ... "id": "${MSGID6}",
- ... "type": "notification",
- ... "resource_id": "${ORGID1}",
- ... "resource_revision": null,
- ... "resource_change": "deleted"
- ... }
-
-The first listener gets no additional notifications.
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID1}/notifications
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... {
- ... "resources": [
- ... {"id": "${MSGID1}"}, {"id": "${MSGID2}"}
- ... ]
- ... }
-
-Deletion of a listener deletes also the notifications.
-
- WHEN client requests DELETE /orgs/listeners/${LISTENID1} with token
- THEN HTTP status code is 200 OK
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID1}/notifications/${MSGID1}
- ... using token
- THEN HTTP status code is 404 Not Found
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID1}/notifications/${MSGID2}
- ... using token
- THEN HTTP status code is 404 Not Found
-
- WHEN client requests GET /orgs/listeners/${LISTENID1} using token
- THEN HTTP status code is 404 Not Found
-
-Notification can be deleted.
-
- WHEN client requests
- ... DELETE /orgs/listeners/${LISTENID2}/notifications/${MSGID3}
- ... with token
- THEN HTTP status code is 200 OK
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID2}/notifications/${MSGID3}
- ... using token
- THEN HTTP status code is 404 Not Found
-
- WHEN client requests
- ... DELETE /orgs/listeners/${LISTENID3}/notifications/${MSGID4}
- ... with token
- THEN HTTP status code is 200 OK
-
- WHEN client requests
- ... GET /orgs/listeners/${LISTENID3}/notifications/${MSGID4}
- ... using token
- THEN HTTP status code is 404 Not Found
-
- WHEN client requests
- ... DELETE /orgs/listeners/${LISTENID2}/notifications/${MSGID5}
- ... with token
- THEN HTTP status code is 200 OK
-
- WHEN client requests
- ... DELETE /orgs/listeners/${LISTENID3}/notifications/${MSGID6}
- ... with token
- THEN HTTP status code is 200 OK
-
-
- FINALLY qvarn is stopped
-
-# Listeners for one resource type only
-
-This scenario tests for a problem found in the first public release of
-Qvarn (JSONB): all listeners would be reported for all resource types,
-intead of only the one they were created for.
-
- SCENARIO notifications only for the right type
-
- GIVEN a running qvarn instance
-
- WHEN client gets an authorization token with scope
- ... "uapi_persons_post
- ... uapi_persons_listeners_post
- ... uapi_persons_listeners_get
- ... uapi_persons_listeners_id_notifications_get
- ... uapi_persons_listeners_id_notifications_id_get
- ... uapi_orgs_post
- ... uapi_orgs_listeners_post
- ... uapi_orgs_listeners_get
- ... uapi_orgs_listeners_id_notifications_get
- ... uapi_orgs_listeners_id_notifications_id_get
- ... "
-
-Make sure there are no listeners yet.
-
- WHEN client requests GET /orgs/listeners using token
- THEN JSON body matches { "resources": [] }
-
- WHEN client requests GET /persons/listeners using token
- THEN JSON body matches { "resources": [] }
-
-Create a listeners for orgs.
-
- WHEN client requests POST /orgs/listeners with token and body
- ... {
- ... "notify_of_new": true
- ... }
- THEN resource id is ORGLISTENER
-
- WHEN client requests GET /orgs/listeners using token
- THEN JSON body matches { "resources": [{ "id": "${ORGLISTENER}" }]}
-
- WHEN client requests GET /persons/listeners using token
- THEN JSON body matches { "resources": [] }
-
-Create a listener for persons.
-
- WHEN client requests POST /persons/listeners with token and body
- ... {
- ... "notify_of_new": true
- ... }
- THEN resource id is PERSONLISTENER
-
- WHEN client requests GET /orgs/listeners using token
- THEN JSON body matches { "resources": [{ "id": "${ORGLISTENER}" }]}
-
- WHEN client requests GET /persons/listeners using token
- THEN JSON body matches { "resources": [{ "id": "${PERSONLISTENER}"}]}
-
-Create a person, make sure only the correct notifications are created.
-
- WHEN client requests POST /persons with token and body
- ... {
- ... "names": [{ "full_name": "James Bond" }]
- ... }
- THEN resource id is PERSONID
-
- WHEN client requests
- ... GET /orgs/listeners/${ORGLISTENER}/notifications
- ... using token
- THEN JSON body matches { "resources": [] }
-
- WHEN client requests
- ... GET /persons/listeners/${PERSONLISTENER}/notifications
- ... using token
- THEN search result at index 0 has id MSGID
-
- WHEN client requests
- ... GET /persons/listeners/${PERSONLISTENER}/notifications/${MSGID}
- ... using token
- THEN JSON body matches
- ... {
- ... "id": "${MSGID}",
- ... "type": "notification",
- ... "resource_id": "${PERSONID}",
- ... "resource_change": "created"
- ... }
-
- FINALLY qvarn is stopped
-
-# Use subresources
-
-Subresources are additional resources that are attached to a resource,
-and be managed and access controlled separetly. However, they share
-existence and revision with their parent: if the parent is deleted, so
-is the subresource, and if either the parent or the subresource is
-changed, and gets a new revision, so does the other.
-
-Subresources always exist, if defined in the resource type spec. They
-don't need to be created specially.
-
- SCENARIO manage subresources
-
- GIVEN a running qvarn instance
-
- WHEN client gets an authorization token with scope
- ... "uapi_subjects_post uapi_subjects_sub_put uapi_subjects_sub_get"
-
- WHEN client requests POST /subjects with token and body
- ... { "type": "subject", "names": [ { "full_name": "Alfred" } ] }
- THEN resource id is ID
- AND revision is REV
-
- WHEN client requests GET /subjects/${ID}/sub using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... {
- ... "subfield": ""
- ... }
-
- WHEN client requests PUT /subjects/${ID}/sub with token and body
- ... {
- ... "revision": "wrong",
- ... "subfield": "Steven Segal"
- ... }
- THEN HTTP status code is 409 Conflict
-
- WHEN client requests PUT /subjects/${ID}/sub with token and body
- ... {
- ... "revision": "${REV}",
- ... "subfield": "Steven Segal"
- ... }
- THEN HTTP status code is 200 OK
- AND revision is REV2
- AND JSON body matches
- ... {
- ... "revision": "${REV2}",
- ... "subfield": "Steven Segal"
- ... }
-
- FINALLY qvarn is stopped
-
-
-# Manage blobs
-
-Blobs are like sub-resources, but they're arbitrary binary data, not
-JSON.
-
- SCENARIO manage blobs
-
- GIVEN a running qvarn instance
-
- WHEN client gets an authorization token with scope
- ... "uapi_subjects_post uapi_subjects_blob_put uapi_subjects_blog_get"
-
-Create a subject.
-
- WHEN client requests POST /subjects with token and body
- ... { "type": "subject", "names": [ { "full_name": "Alfred" } ] }
- THEN resource id is ID
- AND revision is REV1
-
-Newly created subject does not have a blob.
-
- WHEN client requests GET /subjects/${ID}/blob using token
- THEN HTTP status code is 404 Not found
-
-Uploading an empty blob doesn't work.
-
- WHEN client requests PUT /subjects/${ID}/blob with token,
- ... revision REV1,
- ... content-type image/jpeg,
- ... and empty body
- THEN HTTP status code is 411 Length required
-
-Uploading a COMPLETELY VALID JPEG as a blob fails, if subject resource
-revision is wrong.
-
- WHEN client requests PUT /subjects/${ID}/blob with token,
- ... revision BADREV,
- ... content-type image/jpeg,
- ... and body "FAKE JPEG"
- THEN HTTP status code is 409 Conflict
-
-Uploading with valid revision works.
-
- WHEN client requests PUT /subjects/${ID}/blob with token,
- ... revision ${REV1},
- ... content-type image/jpeg,
- ... and body "FAKE JPEG"
- THEN HTTP status code is 200 OK
-
-Do we get the right blob back? Also, note that subject revision
-should've changed.
-
- WHEN client requests GET /subjects/${ID}/blob using token
- THEN HTTP status code is 200 OK
- AND HTTP Content-Type header is image/jpeg
- AND body is "FAKE JPEG"
- AND remember HTTP Revision header as REV2
- AND revisions REV1 and REV2 are different
-
-Uploading with old revision fails.
-
- WHEN client requests PUT /subjects/${ID}/blob with token,
- ... revision ${REV1},
- ... content-type image/jpeg,
- ... and body "FAKE JPEG"
- THEN HTTP status code is 409 Conflict
-
-Uploading a new blob with the current revision works.
-
- WHEN client requests PUT /subjects/${ID}/blob with token,
- ... revision ${REV2},
- ... content-type image/jpeg,
- ... and body "\x89"
- THEN HTTP status code is 200 OK
- AND remember HTTP Revision header as REV3
-
-And it did get updated.
-
- WHEN client requests GET /subjects/${ID}/blob using token
- THEN HTTP status code is 200 OK
- AND HTTP Content-Type header is image/jpeg
- AND body is "\x89"
-
-Updating parent doesn't affect the blob.
-
- WHEN client requests PUT /subjects/${ID} with token and body
- ... {
- ... "type": "subject",
- ... "revision": "${REV3}",
- ... "names": [ { "full_name": "Melissa" } ]
- ... }
- THEN revision is REV4
-
- WHEN client requests GET /subjects/${ID}/blob using token
- THEN HTTP status code is 200 OK
- AND HTTP Content-Type header is image/jpeg
- AND body is "\x89"
- AND remember HTTP Revision header as REV5
- AND revisions REV4 and REV5 match
-
- FINALLY qvarn is stopped
-
-# Search subjects
-
- SCENARIO search subjects
-
- GIVEN a running Qvarn instance
-
- WHEN client gets an authorization token with scope
- ... "uapi_subjects_post uapi_subjects_search_id_get
- ... uapi_subjects_id_delete"
-
- WHEN client requests POST /subjects with token and body
- ... { "type": "subject", "names": [ { "full_name": "Alfred" } ] }
- THEN resource id is ID1
- AND revision is REV1
-
- WHEN client requests POST /subjects with token and body
- ... { "type": "subject", "names": [ { "full_name": "Alfred" } ] }
- THEN resource id is ID2
-
- WHEN client requests POST /subjects with token and body
- ... { "type": "subject", "names": [ { "full_name": "Bruce" } ] }
- THEN resource id is ID3
-
- WHEN client requests GET /subjects/search/exact/full_name/Batman
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches { "resources": []}
-
-Make sure searches are case-insensitive.
-
- WHEN client requests GET /subjects/search/exact/full_name/ALFRED
- ... using token
- THEN HTTP status code is 200 OK
- AND search result contains {"id": "${ID1}"}
- AND search result contains {"id": "${ID2}"}
- AND search result does NOT contain {"id": "${ID3}"}
-
- WHEN client requests GET /subjects/search/exact/full_name/bruce
- ... using token
- THEN HTTP status code is 200 OK
- AND search result does NOT contain {"id": "${ID1}"}
- AND search result does NOT contain {"id": "${ID2}"}
- AND search result contains {"id": "${ID3}"}
- AND JSON body matches { "resources": [{"id": "${ID3}"}]}
-
- WHEN client requests
- ... GET /subjects/search/contains/full_name/fred
- ... using token
- THEN HTTP status code is 200 OK
- AND search result contains {"id": "${ID1}"}
- AND search result contains {"id": "${ID2}"}
- AND search result does NOT contain {"id": "${ID3}"}
-
- WHEN client requests
- ... GET /subjects/search/gt/full_name/Alfred
- ... using token
- THEN HTTP status code is 200 OK
- AND search result does NOT contain {"id": "${ID1}"}
- AND search result does NOT contain {"id": "${ID2}"}
- AND search result contains {"id": "${ID3}"}
-
- WHEN client requests
- ... GET /subjects/search/ge/full_name/Alfred
- ... using token
- THEN HTTP status code is 200 OK
- AND search result contains {"id": "${ID1}"}
- AND search result contains {"id": "${ID2}"}
- AND search result contains {"id": "${ID3}"}
-
- WHEN client requests
- ... GET /subjects/search/startswith/full_name/Alfred
- ... using token
- THEN HTTP status code is 200 OK
- AND search result contains {"id": "${ID1}"}
- AND search result contains {"id": "${ID2}"}
- AND search result does NOT contain {"id": "${ID3}"}
-
- WHEN client requests
- ... GET /subjects/search/show/full_name/contains/full_name/fred
- ... using token
- THEN HTTP status code is 200 OK
- AND search result contains {"id": "${ID1}"}
- AND search result contains {"id": "${ID2}"}
- AND search result does NOT contain {"id": "${ID3}"}
-
- WHEN client requests
- ... GET /subjects/search/show_all/exact/full_name/Bruce
- ... using token
- THEN HTTP status code is 200 OK
- AND search result contains
- ... {
- ... "id": "${ID3}",
- ... "names": [ { "full_name": "Bruce" } ]
- ... }
-
- WHEN client requests
- ... GET /subjects/search/exact/full_name/Br%2Fce
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches { "resources": [] }
-
- GIVEN unique random identifier RANDOM
- WHEN client requests PUT /subjects/${ID1}/sub with token and body
- ... { "subfield": "${RANDOM}", "revision": "${REV1}" }
- THEN HTTP status code is 200 OK
-
- WHEN client requests GET /subjects/search/exact/subfield/${RANDOM} using token
- THEN HTTP status code is 200 OK
- AND search result contains {"id": "${ID1}"}
-
- FINALLY qvarn is stopped
-
-
-# More search
-
-This scenario creates two resources, and searches with two conditions.
-Each condition matches a resource, but the two match different ones.
-Thus the result should be an empty set.
-
- SCENARIO search with two conditions when two resources match one
-
- GIVEN a running Qvarn instance
-
- WHEN client gets an authorization token with scope
- ... "uapi_subjects_post uapi_subjects_search_id_get
- ... uapi_subjects_id_delete"
-
- WHEN client requests POST /subjects with token and body
- ... {
- ... "type": "subject",
- ... "names": [{
- ... "full_name": "Clark Kent",
- ... "sort_key": "Clark the superman"
- ... }]
- ... }
- THEN resource id is ID1
-
- WHEN client requests POST /subjects with token and body
- ... {
- ... "type": "subject",
- ... "names": [{
- ... "full_name": "Clark Celt",
- ... "sort_key": "a nobody"
- ... }]
- ... }
- THEN resource id is ID2
-
- WHEN client requests
- ... GET /subjects/search/contains/full_name/Kent/contains/sort_key/nobody
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches { "resources": [ ]}
-
- WHEN client requests
- ... GET /subjects/search/contains/full_name/Clark
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... {"resources": [{"id": "${ID1}"}, {"id": "${ID2}"}]}
-
- WHEN client requests
- ... GET /subjects/search/contains/full_name/Kent
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches {"resources": [{"id": "${ID1}"}]}
-
- WHEN client requests
- ... GET /subjects/search/contains/sort_key/nobody
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches {"resources": [{"id": "${ID2}"}]}
-
- FINALLY qvarn is stopped
-
-# Searching with multiple conditions
-
- SCENARIO search with multiple conditions
-
- GIVEN a running Qvarn instance
-
- WHEN client gets an authorization token with scope
- ... "uapi_contracts_post uapi_contracts_search_id_get"
-
-Create an organisation, a person and an employment contract between them.
-
- WHEN client requests POST /contracts with token and body
- ... {"contract_type": "employment",
- ... "start_date": "2016-01-01",
- ... "contract_parties": [
- ... {
- ... "type": "org",
- ... "resource_id": "org-1",
- ... "role": "employer"
- ... },
- ... {
- ... "type": "person",
- ... "resource_id": "person-1",
- ... "role": "employee"
- ... }
- ... ],
- ... "contract_state": "active"
- ... }
- THEN HTTP status code is 201 Created
- AND resource id is CONTRACT_ID1
-
-Perform searches matching different instances of the same nested element.
-
- WHEN client requests
- ... GET /contracts/search/exact/resource_id/org-1/exact/resource_id/person-1
- ... using token
- THEN HTTP status code is 200 OK
- AND search result contains {"id": "${CONTRACT_ID1}"}
-
- WHEN client requests
- ... GET /contracts/search/exact/resource_id/org-1/exact/resource_id/wrong_id
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... {
- ... "resources": []
- ... }
-
- FINALLY qvarn is stopped
-
-# Sort search results
-
- SCENARIO search with sort
-
- GIVEN a running Qvarn instance
-
- WHEN client gets an authorization token with scope
- ... "uapi_subjects_post uapi_subjects_search_id_get"
-
-Create several person resources.
-
- GIVEN unique random identifier UID
-
- WHEN client requests POST /subjects with token and body
- ... {
- ... "random_id": "${UID}",
- ... "names": [
- ... {
- ... "full_name": "Person A",
- ... "sort_key": "3, Person",
- ... "given_names": ["Test"],
- ... "surnames": ["Person", "A"]
- ... }
- ... ]
- ... }
- THEN HTTP status code is 201 Created
- THEN resource id is ID3
-
- WHEN client requests POST /subjects with token and body
- ... {
- ... "random_id": "${UID}",
- ... "names": [
- ... {
- ... "full_name": "Person B",
- ... "sort_key": "1, Person",
- ... "given_names": ["Test"],
- ... "surnames": ["Person", "B"]
- ... }
- ... ]
- ... }
- THEN HTTP status code is 201 Created
- THEN resource id is ID1
-
- WHEN client requests POST /subjects with token and body
- ... {
- ... "random_id": "${UID}",
- ... "names": [
- ... {
- ... "full_name": "Person C",
- ... "sort_key": "2, Person",
- ... "given_names": ["Test"],
- ... "surnames": ["Person", "C"]
- ... }
- ... ]
- ... }
- THEN HTTP status code is 201 Created
- THEN resource id is ID2
-
-Search person resources and sort results by `sort_key`.
-
- WHEN client requests
- ... GET /subjects/search/exact/random_id/${UID}/sort/sort_key
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... {
- ... "resources": [
- ... {"id": "${ID1}"},
- ... {"id": "${ID2}"},
- ... {"id": "${ID3}"}
- ... ]
- ... }
-
-Sort person resources using different sort key.
-
- WHEN client requests
- ... GET /subjects/search/exact/random_id/${UID}/sort/full_name
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... {
- ... "resources": [
- ... {"id": "${ID3}"},
- ... {"id": "${ID1}"},
- ... {"id": "${ID2}"}
- ... ]
- ... }
-
-Search person resources and sort results by two search keys, where first search
-key is a list containing more than one value. First key is `surnames`, where
-each resource has same first surname, and second key is `sort_key`. Since each
-first surname is the same, results should fall back to the second sort key.
-
- WHEN client requests
- ... GET /subjects/search/exact/random_id/${UID}/sort/surnames/sort/sort_key
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... {
- ... "resources": [
- ... {"id": "${ID1}"},
- ... {"id": "${ID2}"},
- ... {"id": "${ID3}"}
- ... ]
- ... }
-
-Search with only search operator should also work, returning all available
-resource ids.
-
- WHEN client requests
- ... GET /subjects/search/sort/sort_key
- ... using token
- THEN HTTP status code is 200 OK
- AND search result contains {"id": "${ID1}"}
- AND search result contains {"id": "${ID2}"}
- AND search result contains {"id": "${ID3}"}
-
- FINALLY qvarn is stopped
-
-# Sort search results with /offset and /limit
-
- SCENARIO search with /offset and /limit
-
- GIVEN a running Qvarn instance
-
- WHEN client gets an authorization token with scope
- ... "uapi_subjects_post uapi_subjects_search_id_get"
-
-Create several person resources.
-
- GIVEN unique random identifier UID
-
- WHEN client requests POST /subjects with token and body
- ... {
- ... "random_id": "${UID}",
- ... "names": [
- ... {
- ... "full_name": "Person 1"
- ... }
- ... ]
- ... }
- THEN HTTP status code is 201 Created
- THEN resource id is ID1
-
- WHEN client requests POST /subjects with token and body
- ... {
- ... "random_id": "${UID}",
- ... "names": [
- ... {
- ... "full_name": "Person 2"
- ... }
- ... ]
- ... }
- THEN HTTP status code is 201 Created
- THEN resource id is ID2
-
- WHEN client requests POST /subjects with token and body
- ... {
- ... "random_id": "${UID}",
- ... "names": [
- ... {
- ... "full_name": "Person 3"
- ... }
- ... ]
- ... }
- THEN HTTP status code is 201 Created
- THEN resource id is ID3
-
- WHEN client requests POST /subjects with token and body
- ... {
- ... "random_id": "${UID}",
- ... "names": [
- ... {
- ... "full_name": "Person 4"
- ... }
- ... ]
- ... }
- THEN HTTP status code is 201 Created
- THEN resource id is ID4
-
- WHEN client requests POST /subjects with token and body
- ... {
- ... "random_id": "${UID}",
- ... "names": [
- ... {
- ... "full_name": "Person 5"
- ... }
- ... ]
- ... }
- THEN HTTP status code is 201 Created
- THEN resource id is ID5
-
-Sort, return first two hits.
-
- WHEN client requests
- ... GET /subjects/search/exact/random_id/${UID}/sort/full_name/limit/2
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... {
- ... "resources": [
- ... {"id": "${ID1}"},
- ... {"id": "${ID2}"}
- ... ]
- ... }
-
-Sort, return second set of two hits.
-
- WHEN client requests
- ... GET /subjects/search/exact/random_id/${UID}/sort/full_name/offset/2/limit/2
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... {
- ... "resources": [
- ... {"id": "${ID3}"},
- ... {"id": "${ID4}"}
- ... ]
- ... }
-
-Sort, return third set of two hits, which is actualy only one item.
-
- WHEN client requests
- ... GET /subjects/search/exact/random_id/${UID}/sort/full_name/offset/4/limit/2
- ... using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... {
- ... "resources": [
- ... {"id": "${ID5}"}
- ... ]
- ... }
-
-Don't sort. Then /offset and /limit are verboten.
-
- WHEN client requests
- ... GET /subjects/search/exact/random_id/${UID}/offset/1
- ... using token
- THEN HTTP status code is 400 Error
- AND JSON body matches
- ... {
- ... "message": "LIMIT and OFFSET can only be used with together SORT.",
- ... "error_code": "LimitWithoutSortError"
- ... }
-
- WHEN client requests
- ... GET /subjects/search/exact/random_id/${UID}/offset/1
- ... using token
- THEN HTTP status code is 400 Error
- AND JSON body matches
- ... {
- ... "message": "LIMIT and OFFSET can only be used with together SORT.",
- ... "error_code": "LimitWithoutSortError"
- ... }
-
- FINALLY qvarn is stopped
-
-
-# Handle resource types via API
-
-Qvarn API allows listing and looking at all the resource types it
-knows about. In the future, it will allow manipulaing them as well.
-
- SCENARIO manage resource types
-
- GIVEN a running Qvarn instance
-
- WHEN client gets an authorization token with scope
- ... "uapi_resource_types_get uapi_resource_types_id_get"
-
- WHEN client requests GET /resource_types using token
- THEN HTTP status code is 200 OK
- AND search result contains { "id": "subject" }
-
- WHEN client requests GET /resource_types/subject using token
- THEN HTTP status code is 200 OK
- AND JSON body matches
- ... {
- ... "id": "subject",
- ... "type": "resource_type",
- ... "path": "/subjects"
- ... }
-
- FINALLY qvarn is stopped