Compare commits
47 Commits
lash/updat
...
philip/man
| Author | SHA1 | Date | |
|---|---|---|---|
|
e04d090b06
|
|||
|
122e012329
|
|||
|
f7bfea6563
|
|||
|
ac09875258
|
|||
|
0017247363
|
|||
|
d82d9979a5
|
|||
|
40b1e8272b
|
|||
|
25e4aaf9f6
|
|||
|
c90f1c59ec
|
|||
|
0dbd8d63b2
|
|||
|
9da3b7a099
|
|||
|
fbf736ba98
|
|||
|
8de6e9876e
|
|||
|
b2ab1465d7
|
|||
|
23015f8b98
|
|||
|
ea614e07af
|
|||
|
f35e7010ba
|
|||
|
23ed64836e
|
|||
|
866eb7397b
|
|||
|
a05df2280b
|
|||
|
9e4ad4c650
|
|||
|
5b89c151f9
|
|||
|
5bfe9fcd4a
|
|||
|
2608535200
|
|||
|
f39468d41f
|
|||
|
2be23b9390
|
|||
|
e93851af76
|
|||
|
1e89f01d5f
|
|||
|
6c1c05335e
|
|||
|
5929a6c0bb
|
|||
|
f72313aea9
|
|||
|
43fd7465e5
|
|||
|
30eb9f517b
|
|||
|
f3e06dcd92
|
|||
|
c0f578db75
|
|||
|
5d2e5013f3
|
|||
|
06d9612c6c
|
|||
|
07fef8df40
|
|||
|
1bd281f2a2
|
|||
|
7ab278d098
|
|||
|
15d44c859e
|
|||
|
6d541d38bc
|
|||
|
08567436f2
|
|||
|
091b1e9f16
|
|||
|
85837e1fec
|
|||
|
a17da7b91b
|
|||
|
c8adfb7f19
|
@@ -6,7 +6,6 @@ include:
|
||||
- local: 'apps/cic-notify/.gitlab-ci.yml'
|
||||
- local: 'apps/cic-meta/.gitlab-ci.yml'
|
||||
- local: 'apps/cic-cache/.gitlab-ci.yml'
|
||||
- local: 'apps/data-seeding/.gitlab-ci.yml'
|
||||
|
||||
stages:
|
||||
- build
|
||||
|
||||
@@ -125,7 +125,6 @@ class DataCache(Cache):
|
||||
'to_value': int(r['to_value']),
|
||||
'source_token': r['source_token'],
|
||||
'destination_token': r['destination_token'],
|
||||
'success': r['success'],
|
||||
'tx_type': tx_type,
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ def list_transactions_mined_with_data(
|
||||
:result: Result set
|
||||
:rtype: SQLAlchemy.ResultProxy
|
||||
"""
|
||||
s = "SELECT tx_hash, block_number, date_block, sender, recipient, from_value, to_value, source_token, destination_token, success, domain, value FROM tx LEFT JOIN tag_tx_link ON tx.id = tag_tx_link.tx_id LEFT JOIN tag ON tag_tx_link.tag_id = tag.id WHERE block_number >= {} AND block_number <= {} ORDER BY block_number ASC, tx_index ASC".format(offset, end)
|
||||
s = "SELECT tx_hash, block_number, date_block, sender, recipient, from_value, to_value, source_token, destination_token, domain, value FROM tx LEFT JOIN tag_tx_link ON tx.id = tag_tx_link.tx_id LEFT JOIN tag ON tag_tx_link.tag_id = tag.id WHERE block_number >= {} AND block_number <= {} ORDER BY block_number ASC, tx_index ASC".format(offset, end)
|
||||
|
||||
r = session.execute(s)
|
||||
return r
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
[syncer]
|
||||
loop_interval = 1
|
||||
@@ -5,31 +5,18 @@
|
||||
|
||||
.cic_eth_changes_target:
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
#changes:
|
||||
#- $CONTEXT/$APP_NAME/**/*
|
||||
when: always
|
||||
- changes:
|
||||
- $CONTEXT/$APP_NAME/*
|
||||
|
||||
build-mr-cic-eth:
|
||||
extends:
|
||||
- .cic_eth_variables
|
||||
- .cic_eth_changes_target
|
||||
- .py_build_target_test
|
||||
|
||||
test-mr-cic-eth:
|
||||
extends:
|
||||
- .py_build_merge_request
|
||||
- .cic_eth_variables
|
||||
- .cic_eth_changes_target
|
||||
stage: test
|
||||
image: $CI_REGISTRY_IMAGE/$APP_NAME-test:latest
|
||||
script:
|
||||
- cd apps/$APP_NAME/
|
||||
- pytest tests/unit/
|
||||
- pytest tests/task/
|
||||
- pytest tests/filters/
|
||||
needs: ["build-mr-cic-eth"]
|
||||
|
||||
build-push-cic-eth:
|
||||
extends:
|
||||
- .py_build_push
|
||||
- .cic_eth_variables
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ version = (
|
||||
0,
|
||||
11,
|
||||
0,
|
||||
'beta.14',
|
||||
'beta.13',
|
||||
)
|
||||
|
||||
version_object = semver.VersionInfo(
|
||||
|
||||
@@ -1,62 +1,48 @@
|
||||
FROM python:3.8.6-slim-buster as compile
|
||||
# FROM grassrootseconomics:cic
|
||||
|
||||
#FROM python:3.8.6-alpine
|
||||
FROM python:3.8.6-slim-buster
|
||||
|
||||
#COPY --from=0 /usr/local/share/cic/solidity/ /usr/local/share/cic/solidity/
|
||||
|
||||
WORKDIR /usr/src/cic-eth
|
||||
|
||||
RUN apt-get update && \
|
||||
apt install -y gcc gnupg libpq-dev wget make g++ gnupg bash procps git
|
||||
|
||||
#RUN python -m venv venv && . venv/bin/activate
|
||||
|
||||
ARG pip_extra_index_url_flag='--index https://pypi.org/simple --extra-index-url https://pip.grassrootseconomics.net:8433'
|
||||
RUN /usr/local/bin/python -m pip install --upgrade pip
|
||||
RUN pip install semver
|
||||
|
||||
# TODO use a packaging style that lets us copy requirments only ie. pip-tools
|
||||
COPY cic-eth/ .
|
||||
RUN pip install $pip_extra_index_url_flag .
|
||||
|
||||
# --- TEST IMAGE ---
|
||||
FROM python:3.8.6-slim-buster as test
|
||||
ARG root_requirement_file='requirements.txt'
|
||||
|
||||
#RUN apk update && \
|
||||
# apk add gcc musl-dev gnupg libpq
|
||||
#RUN apk add postgresql-dev
|
||||
#RUN apk add linux-headers
|
||||
#RUN apk add libffi-dev
|
||||
RUN apt-get update && \
|
||||
apt install -y gcc gnupg libpq-dev wget make g++ gnupg bash procps git
|
||||
|
||||
WORKDIR /usr/src/cic-eth
|
||||
|
||||
# Copy shared requirements from top of mono-repo
|
||||
RUN echo "copying root req file: ${root_requirement_file}"
|
||||
#COPY $root_requirement_file .
|
||||
#RUN pip install -r $root_requirement_file $pip_extra_index_url_flag
|
||||
RUN /usr/local/bin/python -m pip install --upgrade pip
|
||||
#RUN git clone https://gitlab.com/grassrootseconomics/cic-base.git && \
|
||||
# cd cic-base && \
|
||||
# git checkout 7ae1f02efc206b13a65873567b0f6d1c3b7f9bc0 && \
|
||||
# python merge_requirements.py | tee merged_requirements.txt
|
||||
#RUN cd cic-base && \
|
||||
# pip install $pip_extra_index_url_flag -r ./merged_requirements.txt
|
||||
RUN pip install $pip_extra_index_url_flag cic-base[full_graph]==0.1.2b9
|
||||
|
||||
COPY --from=compile /usr/local/bin/ /usr/local/bin/
|
||||
COPY --from=compile /usr/local/lib/python3.8/site-packages/ \
|
||||
/usr/local/lib/python3.8/site-packages/
|
||||
# TODO we could use venv inside container to isolate the system and app deps further
|
||||
# COPY --from=compile /usr/src/cic-eth/ .
|
||||
# RUN . venv/bin/activate
|
||||
|
||||
COPY cic-eth/scripts/ scripts/
|
||||
COPY cic-eth/setup.cfg cic-eth/setup.py ./
|
||||
COPY cic-eth/cic_eth/ cic_eth/
|
||||
# Copy app specific requirements
|
||||
COPY cic-eth/requirements.txt .
|
||||
COPY cic-eth/test_requirements.txt .
|
||||
RUN pip install $pip_extra_index_url_flag -r test_requirements.txt
|
||||
|
||||
COPY cic-eth .
|
||||
|
||||
ENV PYTHONPATH .
|
||||
|
||||
ENTRYPOINT ["pytest"]
|
||||
|
||||
# --- RUNTIME ---
|
||||
FROM python:3.8.6-slim-buster as runtime
|
||||
|
||||
RUN apt-get update && \
|
||||
apt install -y gnupg libpq-dev procps
|
||||
|
||||
WORKDIR /usr/src/cic-eth
|
||||
|
||||
COPY --from=compile /usr/local/bin/ /usr/local/bin/
|
||||
COPY --from=compile /usr/local/lib/python3.8/site-packages/ \
|
||||
/usr/local/lib/python3.8/site-packages/
|
||||
RUN pip install $pip_extra_index_url_flag .
|
||||
|
||||
COPY cic-eth/docker/* ./
|
||||
RUN chmod 755 *.sh
|
||||
COPY cic-eth/tests/ tests/
|
||||
|
||||
COPY cic-eth/scripts/ scripts/
|
||||
# # ini files in config directory defines the configurable parameters for the application
|
||||
# # they can all be overridden by environment variables
|
||||
# # to generate a list of environment variables from configuration, use: confini-dump -z <dir> (executable provided by confini package)
|
||||
@@ -65,4 +51,3 @@ COPY cic-eth/cic_eth/db/migrations/ /usr/local/share/cic-eth/alembic/
|
||||
COPY cic-eth/crypto_dev_signer_config/ /usr/local/etc/crypto-dev-signer/
|
||||
|
||||
COPY util/liveness/health.sh /usr/local/bin/health.sh
|
||||
|
||||
|
||||
@@ -22,4 +22,3 @@ sarafu-faucet==0.0.3a3
|
||||
erc20-faucet==0.2.1a4
|
||||
coincurve==15.0.0
|
||||
potaahto~=0.0.1a2
|
||||
pycryptodome==3.10.1
|
||||
|
||||
@@ -11,6 +11,17 @@ while True:
|
||||
requirements.append(l.rstrip())
|
||||
f.close()
|
||||
|
||||
test_requirements = []
|
||||
f = open('test_requirements.txt', 'r')
|
||||
while True:
|
||||
l = f.readline()
|
||||
if l == '':
|
||||
break
|
||||
test_requirements.append(l.rstrip())
|
||||
f.close()
|
||||
|
||||
|
||||
setup(
|
||||
install_requires=requirements
|
||||
install_requires=requirements,
|
||||
tests_require=test_requirements,
|
||||
)
|
||||
|
||||
@@ -4,3 +4,4 @@ pytest-mock==3.3.1
|
||||
pytest-cov==2.10.1
|
||||
eth-tester==0.5.0b3
|
||||
py-evm==0.3.0a20
|
||||
giftable-erc20-token==0.0.8a9
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
const colors = require('colors');
|
||||
const {Meta} = require("../dist");
|
||||
|
||||
let { argv } = require('yargs')
|
||||
.usage('Usage: $0 -m http://localhost:63380 -n publickeys')
|
||||
.example(
|
||||
'$0 -m http://localhost:63380 -n publickeys',
|
||||
'Fetches the public keys blob from the meta server'
|
||||
)
|
||||
.option('m', {
|
||||
alias: 'metaurl',
|
||||
describe: 'The URL for the meta service',
|
||||
demandOption: 'The meta url is required',
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
})
|
||||
.option('n', {
|
||||
alias: 'name',
|
||||
describe: 'The name of the resource to be fetched from the meta service',
|
||||
demandOption: 'The name of the resource is required',
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
})
|
||||
.option('t', {
|
||||
alias: 'type',
|
||||
describe: 'The type of resource to be fetched from the meta service\n' +
|
||||
'Options: `user`, `phone` and `custom`\n' +
|
||||
'Defaults to `custom`',
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
})
|
||||
.epilog('Grassroots Economics (c) 2021')
|
||||
.wrap(null);
|
||||
|
||||
const metaUrl = argv.m;
|
||||
const resourceName = argv.n;
|
||||
let type = argv.t;
|
||||
if (type === undefined) {
|
||||
type = 'custom'
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const identifier = await Meta.getIdentifier(resourceName, type);
|
||||
console.log(colors.cyan(`Meta server storage identifier: ${identifier}`));
|
||||
const metaResponse = await Meta.get(identifier, metaUrl);
|
||||
if (typeof metaResponse !== "object") {
|
||||
console.error(colors.red('Metadata get failed!'));
|
||||
}
|
||||
console.log(colors.green(metaResponse));
|
||||
})();
|
||||
@@ -1,81 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require("fs");
|
||||
const colors = require('colors');
|
||||
const {Meta} = require("../dist");
|
||||
|
||||
let { argv } = require('yargs')
|
||||
.usage('Usage: $0 -m http://localhost:63380 -k ./privatekeys.asc -n publickeys -r ./publickeys.asc')
|
||||
.example(
|
||||
'$0 -m http://localhost:63380 -k ./privatekeys.asc -n publickeys -r ./publickeys.asc',
|
||||
'Updates the public keys blob to the meta server'
|
||||
)
|
||||
.option('m', {
|
||||
alias: 'metaurl',
|
||||
describe: 'The URL for the meta service',
|
||||
demandOption: 'The meta url is required',
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
})
|
||||
.option('k', {
|
||||
alias: 'privatekey',
|
||||
describe: 'The PGP private key blob file used to sign the changes to the meta service',
|
||||
demandOption: 'The private key file is required',
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
})
|
||||
.option('n', {
|
||||
alias: 'name',
|
||||
describe: 'The name of the resource to be set or updated to the meta service',
|
||||
demandOption: 'The name of the resource is required',
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
})
|
||||
.option('r', {
|
||||
alias: 'resource',
|
||||
describe: 'The resource file to be set or updated to the meta service',
|
||||
demandOption: 'The resource file is required',
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
})
|
||||
.option('t', {
|
||||
alias: 'type',
|
||||
describe: 'The type of resource to be set or updated to the meta service\n' +
|
||||
'Options: `user`, `phone` and `custom`\n' +
|
||||
'Defaults to `custom`',
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
})
|
||||
.epilog('Grassroots Economics (c) 2021')
|
||||
.wrap(null);
|
||||
|
||||
const metaUrl = argv.m;
|
||||
const privateKeyFile = argv.k;
|
||||
const resourceName = argv.n;
|
||||
const resourceFile = argv.r;
|
||||
let type = argv.t;
|
||||
if (type === undefined) {
|
||||
type = 'custom'
|
||||
}
|
||||
|
||||
const privateKey = readFile(privateKeyFile);
|
||||
const resource = readFile(resourceFile);
|
||||
|
||||
(async () => {
|
||||
if (privateKey && resource) {
|
||||
const identifier = await Meta.getIdentifier(resourceName, type);
|
||||
console.log(colors.cyan(`Meta server storage identifier: ${identifier}`));
|
||||
const meta = new Meta(metaUrl, privateKey);
|
||||
meta.onload = async (status) => {
|
||||
const response = await meta.set(identifier, resource)
|
||||
console.log(colors.green(response));
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
function readFile(filename) {
|
||||
if(!fs.existsSync(filename)) {
|
||||
console.log(colors.red(`File ${filename} not found`));
|
||||
return;
|
||||
}
|
||||
return fs.readFileSync(filename, {encoding: 'utf8', flag: 'r'});
|
||||
}
|
||||
4232
apps/cic-meta/package-lock.json
generated
4232
apps/cic-meta/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,9 @@
|
||||
{
|
||||
"name": "@cicnet/cic-client-meta",
|
||||
"version": "0.0.11",
|
||||
"name": "cic-client-meta",
|
||||
"version": "0.0.7-alpha.8",
|
||||
"description": "Signed CRDT metadata graphs for the CIC network",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"bin": {
|
||||
"meta-set": "bin/set.js",
|
||||
"meta-get": "bin/get.js"
|
||||
},
|
||||
"preferGlobal": true,
|
||||
"scripts": {
|
||||
"test": "mocha -r node_modules/node-localstorage/register -r ts-node/register tests/*.ts",
|
||||
"build": "node_modules/typescript/bin/tsc -d --outDir dist src/index.ts",
|
||||
@@ -16,14 +11,12 @@
|
||||
"pack": "node_modules/typescript/bin/tsc -d --outDir dist && webpack",
|
||||
"clean": "rm -rf dist",
|
||||
"prepare": "npm run build && npm run build-server",
|
||||
"start": "./node_modules/ts-node/dist/bin.js ./scripts/server/server.ts",
|
||||
"publish": "npm publish --access public"
|
||||
"start": "./node_modules/ts-node/dist/bin.js ./scripts/server/server.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cicnet/crdt-meta": "^0.0.10",
|
||||
"@ethereumjs/tx": "^3.0.0-beta.1",
|
||||
"automerge": "^0.14.1",
|
||||
"colors": "^1.4.0",
|
||||
"crdt-meta": "0.0.8",
|
||||
"ethereumjs-wallet": "^1.0.1",
|
||||
"ini": "^1.3.8",
|
||||
"openpgp": "^4.10.8",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Config } from '@cicnet/crdt-meta';
|
||||
import { Config } from 'crdt-meta';
|
||||
const fs = require('fs');
|
||||
|
||||
if (process.argv[2] === undefined) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as Automerge from 'automerge';
|
||||
import * as pgp from 'openpgp';
|
||||
|
||||
import { Envelope, Syncable } from '@cicnet/crdt-meta';
|
||||
import { Envelope, Syncable } from 'crdt-meta';
|
||||
|
||||
|
||||
function handleNoMergeGet(db, digest, keystore) {
|
||||
@@ -31,7 +31,7 @@ function handleNoMergeGet(db, digest, keystore) {
|
||||
doh(e);
|
||||
});
|
||||
}).catch((e) => {
|
||||
console.error('mesage', e);
|
||||
console.error('message', e);
|
||||
doh(e);
|
||||
});
|
||||
})
|
||||
@@ -46,7 +46,7 @@ function handleServerMergePost(data, db, digest, keystore, signer) {
|
||||
let e = undefined;
|
||||
let s = undefined;
|
||||
if (v === undefined) {
|
||||
s = new Syncable(digest, data);
|
||||
s = new Syncable(digest, o);
|
||||
s.onwrap = (e) => {
|
||||
whohoo(e.toJSON());
|
||||
};
|
||||
|
||||
@@ -3,8 +3,7 @@ import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import * as handlers from './handlers';
|
||||
import { PGPKeyStore, PGPSigner, Config } from '@cicnet/crdt-meta';
|
||||
import { SqliteAdapter, PostgresAdapter } from '../../src/db';
|
||||
import { PGPKeyStore, PGPSigner, Config, SqliteAdapter, PostgresAdapter } from 'crdt-meta';
|
||||
|
||||
import { standardArgs } from './args';
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import {Addressable, mergeKey, Syncable} from "@cicnet/crdt-meta";
|
||||
|
||||
class Custom extends Syncable implements Addressable {
|
||||
|
||||
name: string
|
||||
value: Object
|
||||
|
||||
constructor(name:string, v:Object={}) {
|
||||
super('', v);
|
||||
Custom.toKey(name).then((cid) => {
|
||||
this.id = cid;
|
||||
this.value = v;
|
||||
});
|
||||
}
|
||||
|
||||
public static async toKey(item:string, identifier: string = ':cic.custom') {
|
||||
return await mergeKey(Buffer.from(item), Buffer.from(identifier));
|
||||
}
|
||||
|
||||
public key(): string {
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
Custom,
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
import * as pg from 'pg';
|
||||
import * as sqlite from 'sqlite3';
|
||||
|
||||
type DbConfig = {
|
||||
name: string
|
||||
host: string
|
||||
port: number
|
||||
user: string
|
||||
password: string
|
||||
}
|
||||
|
||||
interface DbAdapter {
|
||||
query: (s:string, callback:(e:any, rs:any) => void) => void
|
||||
close: () => void
|
||||
}
|
||||
|
||||
const re_creatematch = /^(CREATE)/i
|
||||
const re_getmatch = /^(SELECT)/i;
|
||||
const re_setmatch = /^(INSERT|UPDATE)/i;
|
||||
|
||||
class SqliteAdapter implements DbAdapter {
|
||||
|
||||
db: any
|
||||
|
||||
constructor(dbConfig:DbConfig, callback?:(any) => void) {
|
||||
this.db = new sqlite.Database(dbConfig.name); //, callback);
|
||||
}
|
||||
|
||||
public query(s:string, callback:(e:any, rs?:any) => void): void {
|
||||
const local_callback = (e, rs) => {
|
||||
let r = undefined;
|
||||
if (rs !== undefined) {
|
||||
r = {
|
||||
rowCount: rs.length,
|
||||
rows: rs,
|
||||
}
|
||||
}
|
||||
callback(e, r);
|
||||
};
|
||||
if (s.match(re_getmatch)) {
|
||||
this.db.all(s, local_callback);
|
||||
} else if (s.match(re_setmatch)) {
|
||||
this.db.run(s, local_callback);
|
||||
} else if (s.match(re_creatematch)) {
|
||||
this.db.run(s, callback);
|
||||
} else {
|
||||
throw 'unhandled query';
|
||||
}
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.db.close();
|
||||
}
|
||||
}
|
||||
|
||||
class PostgresAdapter implements DbAdapter {
|
||||
|
||||
db: any
|
||||
|
||||
constructor(dbConfig:DbConfig) {
|
||||
let o = dbConfig;
|
||||
o['database'] = o.name;
|
||||
this.db = new pg.Pool(o);
|
||||
return this.db;
|
||||
}
|
||||
|
||||
public query(s:string, callback:(e:any, rs:any) => void): void {
|
||||
this.db.query(s, (e, rs) => {
|
||||
let r = {
|
||||
length: rs.rowCount,
|
||||
}
|
||||
rs.length = rs.rowCount;
|
||||
if (e === undefined) {
|
||||
e = null;
|
||||
}
|
||||
console.debug(e, rs);
|
||||
callback(e, rs);
|
||||
});
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.db.end();
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
DbConfig,
|
||||
SqliteAdapter,
|
||||
PostgresAdapter,
|
||||
}
|
||||
@@ -1,4 +1,2 @@
|
||||
export { User } from './user';
|
||||
export { Phone } from './phone';
|
||||
export { Custom } from './custom';
|
||||
export { Meta } from './meta';
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
import {ArgPair, Envelope, Syncable, MutablePgpKeyStore, PGPSigner} from "@cicnet/crdt-meta";
|
||||
import {User} from "./user";
|
||||
import {Phone} from "./phone";
|
||||
import {Custom} from "./custom";
|
||||
const fetch = require("node-fetch");
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json;charset=utf-8',
|
||||
'x-cic-automerge': 'client'
|
||||
};
|
||||
const options = {
|
||||
headers: headers,
|
||||
};
|
||||
|
||||
class Meta {
|
||||
keystore: MutablePgpKeyStore = new MutablePgpKeyStore();
|
||||
signer: PGPSigner = new PGPSigner(this.keystore);
|
||||
metaUrl: string;
|
||||
private privateKey: string;
|
||||
onload: (status: boolean) => void;
|
||||
|
||||
constructor(metaUrl: string, privateKey: any) {
|
||||
this.metaUrl = metaUrl;
|
||||
this.privateKey = privateKey;
|
||||
this.keystore.loadKeyring().then(() => {
|
||||
this.keystore.importPrivateKey(privateKey).then(() => this.onload(true));
|
||||
});
|
||||
}
|
||||
|
||||
async set(identifier: string, data: Object): Promise<any> {
|
||||
let syncable: Syncable;
|
||||
const response = await Meta.get(identifier, this.metaUrl);
|
||||
if (response === `Request to ${this.metaUrl}/${identifier} failed. Connection error.`) {
|
||||
return response;
|
||||
} else if (typeof response !== "object" || typeof data !== "object") {
|
||||
syncable = new Syncable(identifier, data);
|
||||
const res = await this.updateMeta(syncable, identifier);
|
||||
return `${res.status}: ${res.statusText}`;
|
||||
} else {
|
||||
syncable = await Meta.get(identifier, this.metaUrl);
|
||||
let update: Array<ArgPair> = [];
|
||||
for (const prop in data) {
|
||||
update.push(new ArgPair(prop, data[prop]));
|
||||
}
|
||||
syncable.update(update, 'client-branch');
|
||||
const res = await this.updateMeta(syncable, identifier);
|
||||
return `${res.status}: ${res.statusText}`;
|
||||
}
|
||||
}
|
||||
|
||||
async updateMeta(syncable: Syncable, identifier: string): Promise<any> {
|
||||
const envelope: Envelope = await this.wrap(syncable);
|
||||
const reqBody: string = envelope.toJSON();
|
||||
const putOptions = {
|
||||
method: 'PUT',
|
||||
headers: headers,
|
||||
body: reqBody
|
||||
};
|
||||
return await fetch(`${this.metaUrl}/${identifier}`, putOptions).then(async response => {
|
||||
if (response.ok) {
|
||||
return Promise.resolve({
|
||||
status: response.status,
|
||||
statusText: response.statusText + ', Metadata updated successfully!'
|
||||
});
|
||||
} else {
|
||||
return Promise.reject({
|
||||
status: response.status,
|
||||
statusText: response.statusText
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static async get(identifier: string, metaUrl: string): Promise<any> {
|
||||
const response = await fetch(`${metaUrl}/${identifier}`, options).then(response => {
|
||||
if (response.ok) {
|
||||
return (response.json());
|
||||
} else {
|
||||
return Promise.reject({
|
||||
status: response.status,
|
||||
statusText: response.statusText
|
||||
});
|
||||
}
|
||||
}).catch(error => {
|
||||
if (error.code === 'ECONNREFUSED') {
|
||||
return `Request to ${metaUrl}/${identifier} failed. Connection error.`
|
||||
}
|
||||
return `${error.status}: ${error.statusText}`;
|
||||
});
|
||||
if (typeof response !== "object") {
|
||||
return response;
|
||||
}
|
||||
return Envelope.fromJSON(JSON.stringify(response)).unwrap();
|
||||
}
|
||||
|
||||
static async getIdentifier(name: string, type: string = 'custom'): Promise<string> {
|
||||
let identifier: string;
|
||||
type = type.toLowerCase();
|
||||
if (type === 'user') {
|
||||
identifier = await User.toKey(name);
|
||||
} else if (type === 'phone') {
|
||||
identifier = await Phone.toKey(name);
|
||||
} else {
|
||||
identifier = await Custom.toKey(name);
|
||||
}
|
||||
return identifier;
|
||||
}
|
||||
|
||||
private wrap(syncable: Syncable): Promise<Envelope> {
|
||||
return new Promise<Envelope>(async (resolve, reject) => {
|
||||
syncable.setSigner(this.signer);
|
||||
syncable.onwrap = async (env) => {
|
||||
if (env === undefined) {
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
resolve(env);
|
||||
};
|
||||
syncable.sign();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
Meta,
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Syncable, Addressable, mergeKey } from '@cicnet/crdt-meta';
|
||||
import { Syncable, Addressable, mergeKey } from 'crdt-meta';
|
||||
|
||||
class Phone extends Syncable implements Addressable {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Syncable, Addressable, toAddressKey } from '@cicnet/crdt-meta';
|
||||
import { Syncable, Addressable, toAddressKey } from 'crdt-meta';
|
||||
|
||||
const keySalt = new TextEncoder().encode(':cic.person');
|
||||
class User extends Syncable implements Addressable {
|
||||
|
||||
@@ -4,8 +4,7 @@ import pgp = require('openpgp');
|
||||
import sqlite = require('sqlite3');
|
||||
|
||||
import * as handlers from '../scripts/server/handlers';
|
||||
import { Envelope, Syncable, ArgPair, PGPKeyStore, PGPSigner, KeyStore, Signer } from '@cicnet/crdt-meta';
|
||||
import { SqliteAdapter } from '../src/db';
|
||||
import { Envelope, Syncable, ArgPair, PGPKeyStore, PGPSigner, KeyStore, Signer, SqliteAdapter } from 'crdt-meta';
|
||||
|
||||
function createKeystore() {
|
||||
const pksa = fs.readFileSync(__dirname + '/privatekeys.asc', 'utf-8');
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"scripts/server/*",
|
||||
"index.ts",
|
||||
"bin"
|
||||
"index.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ class AfricasTalkingNotifier:
|
||||
response = self.api_client.send(message=message, recipients=[recipient])
|
||||
logg.debug(f'africastalking response no-sender-id {response}')
|
||||
|
||||
recipients = response.get('Recipients')
|
||||
recipients = response.get('SMSMessageData').get('Recipients')
|
||||
|
||||
if len(recipients) != 1:
|
||||
status = response.get('SMSMessageData').get('Message')
|
||||
|
||||
@@ -28,6 +28,7 @@ packages =
|
||||
cic_notify
|
||||
cic_notify.db
|
||||
cic_notify.db.models
|
||||
cic_notify.ext
|
||||
cic_notify.tasks.sms
|
||||
cic_notify.runnable
|
||||
scripts =
|
||||
|
||||
4
apps/cic-ussd/.config/test/integration.ini
Normal file
4
apps/cic-ussd/.config/test/integration.ini
Normal file
@@ -0,0 +1,4 @@
|
||||
[test]
|
||||
gift_value = 50.00
|
||||
server_url = http://localhost:63315/
|
||||
token_symbol = GFT
|
||||
@@ -238,13 +238,43 @@
|
||||
"description": "Menu to display a user's entire profile",
|
||||
"display_key": "ussd.kenya.display_user_metadata",
|
||||
"name": "display_user_metadata",
|
||||
"parent": "account_management"
|
||||
"parent": "metadata_management"
|
||||
},
|
||||
"41": {
|
||||
"description": "The recipient is not in the system",
|
||||
"display_key": "ussd.kenya.exit_invalid_recipient",
|
||||
"name": "exit_invalid_recipient",
|
||||
"parent": null
|
||||
},
|
||||
"42": {
|
||||
"description": "Pin entry menu for changing name data.",
|
||||
"display_key": "ussd.kenya.name_edit_pin_authorization",
|
||||
"name": "name_edit_pin_authorization",
|
||||
"parent": "metadata_management"
|
||||
},
|
||||
"43": {
|
||||
"description": "Pin entry menu for changing gender data.",
|
||||
"display_key": "ussd.kenya.gender_edit_pin_authorization",
|
||||
"name": "gender_edit_pin_authorization",
|
||||
"parent": "metadata_management"
|
||||
},
|
||||
"44": {
|
||||
"description": "Pin entry menu for changing location data.",
|
||||
"display_key": "ussd.kenya.location_edit_pin_authorization",
|
||||
"name": "location_edit_pin_authorization",
|
||||
"parent": "metadata_management"
|
||||
},
|
||||
"45": {
|
||||
"description": "Pin entry menu for changing products data.",
|
||||
"display_key": "ussd.kenya.products_edit_pin_authorization",
|
||||
"name": "products_edit_pin_authorization",
|
||||
"parent": "metadata_management"
|
||||
},
|
||||
"46": {
|
||||
"description": "Pin confirmation for pin change.",
|
||||
"display_key": "ussd.kenya.new_pin_confirmation",
|
||||
"name": "new_pin_confirmation",
|
||||
"parent": "metadata_management"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -78,28 +78,27 @@ class MetadataRequestsHandler(Metadata):
|
||||
:param data: The data to be stored in the metadata server.
|
||||
:type data: dict|str
|
||||
"""
|
||||
data = json.dumps(data).encode('utf-8')
|
||||
data = json.dumps(data)
|
||||
result = make_request(method='POST', url=self.url, data=data, headers=self.headers)
|
||||
metadata_http_error_handler(result=result)
|
||||
metadata = result.content
|
||||
metadata = result.json()
|
||||
self.edit(data=metadata)
|
||||
|
||||
def edit(self, data: bytes):
|
||||
def edit(self, data: Union[Dict, str]):
|
||||
""" This function is responsible for editing data in the metadata server corresponding to a unique pointer.
|
||||
:param data: The data to be edited in the metadata server.
|
||||
:type data: bytes
|
||||
:type data: dict
|
||||
"""
|
||||
cic_meta_signer = Signer()
|
||||
signature = cic_meta_signer.sign_digest(data=data)
|
||||
algorithm = cic_meta_signer.get_operational_key().get('algo')
|
||||
decoded_data = data.decode('utf-8')
|
||||
formatted_data = {
|
||||
'm': data.decode('utf-8'),
|
||||
'm': json.dumps(data),
|
||||
's': {
|
||||
'engine': self.engine,
|
||||
'algo': algorithm,
|
||||
'data': signature,
|
||||
'digest': json.loads(data).get('digest'),
|
||||
'digest': data.get('digest'),
|
||||
}
|
||||
}
|
||||
formatted_data = json.dumps(formatted_data)
|
||||
@@ -110,19 +109,32 @@ class MetadataRequestsHandler(Metadata):
|
||||
decoded_identifier = self.identifier.decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
decoded_identifier = self.identifier.hex()
|
||||
logg.info(f'identifier: {decoded_identifier}. metadata pointer: {self.metadata_pointer} set to: {decoded_data}.')
|
||||
logg.info(f'identifier: {decoded_identifier}. metadata pointer: {self.metadata_pointer} set to: {data}.')
|
||||
|
||||
def query(self):
|
||||
"""This function is responsible for querying the metadata server for data corresponding to a unique pointer."""
|
||||
"""
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
# retrieve the metadata
|
||||
result = make_request(method='GET', url=self.url)
|
||||
metadata_http_error_handler(result=result)
|
||||
response_data = result.json()
|
||||
data = json.loads(response_data)
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError(f'Invalid data object: {data}.')
|
||||
|
||||
# json serialize retrieved data
|
||||
result_data = result.json()
|
||||
|
||||
# validate result data format
|
||||
if not isinstance(result_data, dict):
|
||||
raise ValueError(f'Invalid result data object: {result_data}.')
|
||||
|
||||
if result.status_code == 200 and self.cic_type == ':cic.person':
|
||||
# validate person metadata
|
||||
person = Person()
|
||||
deserialized_person = person.deserialize(person_data=data)
|
||||
data = json.dumps(deserialized_person.serialize())
|
||||
cache_data(self.metadata_pointer, data=data)
|
||||
person_data = person.deserialize(person_data=result_data)
|
||||
|
||||
# format new person data for caching
|
||||
data = json.dumps(person_data.serialize())
|
||||
|
||||
# cache metadata
|
||||
cache_data(key=self.metadata_pointer, data=data)
|
||||
logg.debug(f'caching: {data} with key: {self.metadata_pointer}')
|
||||
|
||||
@@ -47,14 +47,13 @@ class Signer:
|
||||
logg.debug(f'using signing key: {key_id}, algorithm: {key_algorithm}')
|
||||
return gpg_keys[0]
|
||||
|
||||
def sign_digest(self, data: bytes):
|
||||
def sign_digest(self, data: dict):
|
||||
"""
|
||||
:param data:
|
||||
:type data:
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
data = json.loads(data)
|
||||
digest = data['digest']
|
||||
key_id = self.get_operational_key().get('keyid')
|
||||
signature = self.gpg.sign(digest, passphrase=self.gpg_passphrase, keyid=key_id)
|
||||
|
||||
@@ -251,9 +251,9 @@ def process_display_user_metadata(user: Account, display_key: str):
|
||||
identifier=blockchain_address_to_metadata_pointer(blockchain_address=user.blockchain_address),
|
||||
cic_type=':cic.person'
|
||||
)
|
||||
user_metadata = get_cached_data(key)
|
||||
if user_metadata:
|
||||
user_metadata = json.loads(user_metadata)
|
||||
cached_metadata = get_cached_data(key)
|
||||
if cached_metadata:
|
||||
user_metadata = json.loads(cached_metadata)
|
||||
contact_data = get_contact_data_from_vcard(vcard=user_metadata.get('vcard'))
|
||||
logg.debug(f'{contact_data}')
|
||||
full_name = f'{contact_data.get("given")} {contact_data.get("family")}'
|
||||
@@ -433,7 +433,8 @@ def process_request(user_input: str, user: Account, ussd_session: Optional[dict]
|
||||
'exit_invalid_pin',
|
||||
'exit_invalid_new_pin',
|
||||
'exit_pin_mismatch',
|
||||
'exit_invalid_request'
|
||||
'exit_invalid_request',
|
||||
'exit_successful_transaction'
|
||||
] and person_metadata is not None:
|
||||
return UssdMenu.find_by_name(name='start')
|
||||
else:
|
||||
|
||||
@@ -13,7 +13,7 @@ import bcrypt
|
||||
|
||||
# local imports
|
||||
from cic_ussd.db.models.account import AccountStatus, Account
|
||||
from cic_ussd.encoder import PasswordEncoder, create_password_hash
|
||||
from cic_ussd.encoder import PasswordEncoder, create_password_hash, check_password_hash
|
||||
from cic_ussd.operations import persist_session_to_db_task, create_or_update_session
|
||||
from cic_ussd.redis import InMemoryStore
|
||||
|
||||
@@ -78,9 +78,13 @@ def save_initial_pin_to_session_data(state_machine_data: Tuple[str, dict, Accoun
|
||||
|
||||
# set initial pin data
|
||||
initial_pin = create_password_hash(user_input)
|
||||
session_data = {
|
||||
'initial_pin': initial_pin
|
||||
}
|
||||
if ussd_session.get('session_data'):
|
||||
session_data = ussd_session.get('session_data')
|
||||
session_data['initial_pin'] = initial_pin
|
||||
else:
|
||||
session_data = {
|
||||
'initial_pin': initial_pin
|
||||
}
|
||||
|
||||
# create new in memory ussd session with current ussd session data
|
||||
create_or_update_session(
|
||||
@@ -103,9 +107,8 @@ def pins_match(state_machine_data: Tuple[str, dict, Account]) -> bool:
|
||||
"""
|
||||
user_input, ussd_session, user = state_machine_data
|
||||
initial_pin = ussd_session.get('session_data').get('initial_pin')
|
||||
fernet = PasswordEncoder(PasswordEncoder.key)
|
||||
initial_pin = fernet.decrypt(initial_pin.encode())
|
||||
return bcrypt.checkpw(user_input.encode(), initial_pin)
|
||||
logg.debug(f'USSD SESSION: {ussd_session}')
|
||||
return check_password_hash(user_input, initial_pin)
|
||||
|
||||
|
||||
def complete_pin_change(state_machine_data: Tuple[str, dict, Account]):
|
||||
|
||||
@@ -64,13 +64,17 @@ def process_gender_user_input(user: Account, user_input: str):
|
||||
if user.preferred_language == 'en':
|
||||
if user_input == '1':
|
||||
gender = 'Male'
|
||||
else:
|
||||
elif user_input == '2':
|
||||
gender = 'Female'
|
||||
elif user_input == '3':
|
||||
gender = 'Other'
|
||||
else:
|
||||
if user_input == '1':
|
||||
gender = 'Mwanaume'
|
||||
else:
|
||||
elif user_input == '2':
|
||||
gender = 'Mwanamke'
|
||||
elif user_input == '3':
|
||||
gender = 'Nyingine'
|
||||
return gender
|
||||
|
||||
|
||||
@@ -88,14 +92,18 @@ def save_metadata_attribute_to_session_data(state_machine_data: Tuple[str, dict,
|
||||
key = ''
|
||||
if 'given_name' in current_state:
|
||||
key = 'given_name'
|
||||
elif 'family_name' in current_state:
|
||||
|
||||
if 'family_name' in current_state:
|
||||
key = 'family_name'
|
||||
elif 'gender' in current_state:
|
||||
|
||||
if 'gender' in current_state:
|
||||
key = 'gender'
|
||||
user_input = process_gender_user_input(user=user, user_input=user_input)
|
||||
elif 'location' in current_state:
|
||||
|
||||
if 'location' in current_state:
|
||||
key = 'location'
|
||||
elif 'products' in current_state:
|
||||
|
||||
if 'products' in current_state:
|
||||
key = 'products'
|
||||
|
||||
# check if there is existing session data
|
||||
@@ -121,12 +129,20 @@ def format_user_metadata(metadata: dict, user: Account):
|
||||
gender = metadata.get('gender')
|
||||
given_name = metadata.get('given_name')
|
||||
family_name = metadata.get('family_name')
|
||||
location = {
|
||||
"area_name": metadata.get('location')
|
||||
}
|
||||
products = []
|
||||
if metadata.get('products'):
|
||||
|
||||
# check whether there's existing location data
|
||||
if isinstance(metadata.get('location'), dict):
|
||||
location = metadata.get('location')
|
||||
else:
|
||||
location = {
|
||||
"area_name": metadata.get('location')
|
||||
}
|
||||
# check whether it is a list
|
||||
if isinstance(metadata.get('products'), list):
|
||||
products = metadata.get('products')
|
||||
else:
|
||||
products = metadata.get('products').split(',')
|
||||
|
||||
phone_number = user.phone_number
|
||||
date_registered = int(user.created.replace().timestamp())
|
||||
blockchain_address = user.blockchain_address
|
||||
@@ -192,28 +208,27 @@ def edit_user_metadata_attribute(state_machine_data: Tuple[str, dict, Account]):
|
||||
# validate user metadata
|
||||
person = Person()
|
||||
user_metadata = json.loads(user_metadata)
|
||||
deserialized_person = person.deserialize(person_data=user_metadata)
|
||||
|
||||
# edit specific metadata attribute
|
||||
if given_name:
|
||||
deserialized_person.given_name = given_name
|
||||
elif family_name:
|
||||
deserialized_person.family_name = family_name
|
||||
elif gender:
|
||||
deserialized_person.gender = gender
|
||||
elif location:
|
||||
user_metadata['given_name'] = given_name
|
||||
if family_name:
|
||||
user_metadata['family_name'] = family_name
|
||||
if gender:
|
||||
user_metadata['gender'] = gender
|
||||
if location:
|
||||
# get existing location metadata:
|
||||
location_data = user_metadata.get('location')
|
||||
location_data['area_name'] = location
|
||||
deserialized_person.location = location_data
|
||||
elif products:
|
||||
deserialized_person.products = products
|
||||
user_metadata['location'] = location_data
|
||||
if products:
|
||||
user_metadata['products'] = products
|
||||
|
||||
edited_metadata = deserialized_person.serialize()
|
||||
user_metadata = format_user_metadata(metadata=user_metadata, user=user)
|
||||
|
||||
s_edit_person_metadata = celery.signature(
|
||||
'cic_ussd.tasks.metadata.edit_person_metadata',
|
||||
[blockchain_address, edited_metadata]
|
||||
'cic_ussd.tasks.metadata.create_person_metadata',
|
||||
[blockchain_address, user_metadata]
|
||||
)
|
||||
s_edit_person_metadata.apply_async(queue='cic-ussd')
|
||||
|
||||
|
||||
@@ -19,4 +19,6 @@ def translation_for(key: str, preferred_language: Optional[str] = None, **kwargs
|
||||
"""
|
||||
if preferred_language:
|
||||
i18n.set('locale', preferred_language)
|
||||
else:
|
||||
i18n.set('locale', i18n.config.get('fallback'))
|
||||
return i18n.t(key, **kwargs)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
cic_base[full_graph]~=0.1.2b2
|
||||
cic-eth~=0.11.0b9
|
||||
cic-notify~=0.4.0a4
|
||||
cic-notify~=0.4.0a5
|
||||
cic-types~=0.1.0a10
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
"enter_new_pin",
|
||||
"new_pin_confirmation",
|
||||
"display_user_metadata",
|
||||
"standard_pin_authorization",
|
||||
"name_edit_pin_authorization",
|
||||
"gender_edit_pin_authorization",
|
||||
"location_edit_pin_authorization",
|
||||
"products_edit_pin_authorization",
|
||||
"account_balances_pin_authorization",
|
||||
"account_statement_pin_authorization",
|
||||
"account_balances"
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
pytest==6.0.1
|
||||
Faker==8.1.2
|
||||
faker-e164==0.1.0
|
||||
pytest==6.2.4
|
||||
pytest-alembic==0.2.5
|
||||
pytest-celery==0.0.0a1
|
||||
pytest-cov==2.10.1
|
||||
pytest-mock==3.3.1
|
||||
pytest-ordering==0.6
|
||||
pytest-redis==2.0.0
|
||||
requests-mock==1.8.0
|
||||
requests-mock==1.8.0
|
||||
tavern==1.14.2
|
||||
@@ -6,6 +6,7 @@ from cic_types.pytest import *
|
||||
from tests.fixtures.config import *
|
||||
from tests.fixtures.db import *
|
||||
from tests.fixtures.celery import *
|
||||
from tests.fixtures.integration import *
|
||||
from tests.fixtures.user import *
|
||||
from tests.fixtures.ussd_session import *
|
||||
from tests.fixtures.redis import *
|
||||
|
||||
249
apps/cic-ussd/tests/fixtures/integration.py
vendored
Normal file
249
apps/cic-ussd/tests/fixtures/integration.py
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
# standard imports
|
||||
|
||||
# external imports
|
||||
|
||||
import pytest
|
||||
from faker import Faker
|
||||
|
||||
# local imports
|
||||
|
||||
# test imports
|
||||
from tests.helpers.accounts import phone_number, pin_number, session_id
|
||||
|
||||
|
||||
fake = Faker()
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def generate_phone_number() -> str:
|
||||
return phone_number()
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def generate_session_id() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_account_phone_number() -> str:
|
||||
return phone_number()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_account_phone_number() -> str:
|
||||
return phone_number()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_account_pin_number() -> str:
|
||||
return pin_number()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_account_pin_number() -> str:
|
||||
return pin_number()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_metadata_entry_session_id() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_metadata_entry_session_id() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_transaction_session_id() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_transaction_session_id() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_account_given_name() -> str:
|
||||
return fake.first_name()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_account_given_name() -> str:
|
||||
return fake.first_name()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_account_family_name() -> str:
|
||||
return fake.last_name()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_account_family_name() -> str:
|
||||
return fake.last_name()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_account_location() -> str:
|
||||
return fake.city()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_account_location() -> str:
|
||||
return fake.city()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_account_product() -> str:
|
||||
return fake.color_name()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_account_product() -> str:
|
||||
return fake.color_name()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_account_verify_balance_session_id() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_account_verify_balance_session_id() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_profile_management_session_id() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_profile_management_session_id() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_account_change_given_name() -> str:
|
||||
return fake.first_name()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_account_change_given_name() -> str:
|
||||
return fake.first_name()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_account_change_family_name() -> str:
|
||||
return fake.last_name()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_account_change_family_name() -> str:
|
||||
return fake.last_name()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_account_change_location() -> str:
|
||||
return fake.city()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_account_change_location() -> str:
|
||||
return fake.city()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_account_change_product() -> str:
|
||||
return fake.color_name()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_account_change_product() -> str:
|
||||
return fake.color_name()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_profile_management_session_id_1() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_profile_management_session_id_1() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_profile_management_session_id_2() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_profile_management_session_id_2() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_profile_management_session_id_3() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_profile_management_session_id_3() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_profile_management_session_id_4() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_profile_management_session_id_4() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_account_management_session_id() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_account_management_session_id() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_account_management_session_id_1() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_account_management_session_id_1() -> str:
|
||||
return session_id()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def first_account_new_pin_number() -> str:
|
||||
return pin_number()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def second_account_new_pin_number() -> str:
|
||||
return pin_number()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def gift_value(load_config):
|
||||
return load_config.get('TEST_GIFT_VALUE')
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def server_url(load_config):
|
||||
return load_config.get('TEST_SERVER_URL')
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def token_symbol(load_config):
|
||||
return load_config.get('TEST_TOKEN_SYMBOL')
|
||||
26
apps/cic-ussd/tests/helpers/accounts.py
Normal file
26
apps/cic-ussd/tests/helpers/accounts.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# standard imports
|
||||
import random
|
||||
import uuid
|
||||
|
||||
# external imports
|
||||
from faker import Faker
|
||||
from faker_e164.providers import E164Provider
|
||||
|
||||
# local imports
|
||||
|
||||
# test imports
|
||||
|
||||
fake = Faker()
|
||||
fake.add_provider(E164Provider)
|
||||
|
||||
|
||||
def phone_number() -> str:
|
||||
return fake.e164('KE')
|
||||
|
||||
|
||||
def session_id() -> str:
|
||||
return uuid.uuid4().hex
|
||||
|
||||
|
||||
def pin_number() -> int:
|
||||
return random.randint(1000, 9999)
|
||||
11
apps/cic-ussd/tests/integration/ext/validator.py
Normal file
11
apps/cic-ussd/tests/integration/ext/validator.py
Normal file
@@ -0,0 +1,11 @@
|
||||
import logging
|
||||
|
||||
|
||||
logg = logging.getLogger()
|
||||
logg.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
def validate_response(response, expected_response):
|
||||
"""Makes sure that the response received matches the expected response"""
|
||||
logg.debug(f'RESPONSE: {response.content.decode("utf-8")}')
|
||||
assert response.content.decode('utf-8') == expected_response
|
||||
2
apps/cic-ussd/tests/integration/run.sh
Normal file
2
apps/cic-ussd/tests/integration/run.sh
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
PYTHONPATH=. py.test --debug -vv --log-level debug -s --log-cli-level debug
|
||||
@@ -0,0 +1,466 @@
|
||||
test_name: Test the creation of accounts through the cic_user_ussd_server entrypoint.
|
||||
marks:
|
||||
- usefixtures:
|
||||
- gift_value
|
||||
- server_url
|
||||
- token_symbol
|
||||
- generate_session_id
|
||||
- first_account_phone_number
|
||||
- second_account_phone_number
|
||||
- first_account_pin_number
|
||||
- second_account_pin_number
|
||||
- first_account_family_name
|
||||
- second_account_family_name
|
||||
- first_account_given_name
|
||||
- second_account_given_name
|
||||
- first_account_location
|
||||
- second_account_location
|
||||
- first_account_product
|
||||
- second_account_product
|
||||
- first_metadata_entry_session_id
|
||||
- second_metadata_entry_session_id
|
||||
- first
|
||||
|
||||
stages:
|
||||
- name: Initiate account creation process [first account].
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{generate_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: ""
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '175'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "END Your account is being created. You will receive an SMS when your account is ready.\nAkaunti yako ya Sarafu inatayarishwa. Utapokea ujumbe wa SMS akaunti yako ikiwa tayari.\n"
|
||||
|
||||
- name: Initiate account creation process [second account].
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{generate_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: ""
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '175'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "END Your account is being created. You will receive an SMS when your account is ready.\nAkaunti yako ya Sarafu inatayarishwa. Utapokea ujumbe wa SMS akaunti yako ikiwa tayari.\n"
|
||||
delay_after: 5
|
||||
|
||||
- name: Initaite account metadata entry [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_metadata_entry_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: ""
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '61'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Welcome to Sarafu Network\n1. English\n2. Kiswahili\n3. Help"
|
||||
|
||||
- name: Initaite account metadata entry [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_metadata_entry_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: ""
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '61'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Welcome to Sarafu Network\n1. English\n2. Kiswahili\n3. Help"
|
||||
|
||||
- name: Select preferred language [English]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_metadata_entry_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "1"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '64'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Please enter a new four number PIN for your account.\n0. Back"
|
||||
|
||||
- name: Select preferred language [Kiswahili]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_metadata_entry_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '71'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Tafadhali weka pin mpya yenye nambari nne kwa akaunti yako\n0. Nyuma"
|
||||
|
||||
- name: Enter pin number [{first_account_pin_number} - first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_metadata_entry_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "1*{first_account_pin_number}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '44'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Enter your four number PIN again\n0. Back"
|
||||
|
||||
- name: Enter pin number [second_account_pin_number - second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_metadata_entry_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2*{second_account_pin_number}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '31'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Weka PIN yako tena\n0. Nyuma"
|
||||
|
||||
- name: Pin number confirmation [first_account_pin_number - first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_metadata_entry_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "1*{first_account_pin_number}*{first_account_pin_number}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '28'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Enter first name\n0. Back"
|
||||
|
||||
- name: Pin number confirmation [{second_account_pin_number} - second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_metadata_entry_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2*{second_account_pin_number}*{second_account_pin_number}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '37'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Weka jina lako la kwanza\n0. Nyuma"
|
||||
|
||||
- name: Enter first name [first_account_given_name - first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_metadata_entry_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "1*{first_account_pin_number}*{first_account_pin_number}*{first_account_given_name}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '29'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Enter family name\n0. Back"
|
||||
|
||||
- name: Enter first name [second_account_given_name - second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_metadata_entry_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2*{second_account_pin_number}*{second_account_pin_number}*{second_account_given_name}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '37'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Weka jina lako la mwisho\n0. Nyuma"
|
||||
|
||||
- name: Enter last name [first_account_family_name - first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_metadata_entry_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "1*{first_account_pin_number}*{first_account_pin_number}*{first_account_given_name}*{first_account_family_name}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '51'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Enter gender\n1. Male\n2. Female\n3. Other\n0. Back"
|
||||
|
||||
- name: Enter last name [second_account_family_name - second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_metadata_entry_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2*{second_account_pin_number}*{second_account_pin_number}*{second_account_given_name}*{second_account_family_name}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '64'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Weka jinsia yako\n1. Mwanaume\n2. Mwanamke\n3. Nyngine\n0. Nyuma"
|
||||
|
||||
- name: Select gender [Male - first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_metadata_entry_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "1*{first_account_pin_number}*{first_account_pin_number}*{first_account_given_name}*{first_account_family_name}*1"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '31'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Enter your location\n0. Back"
|
||||
|
||||
- name: Select gender [Female - second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_metadata_entry_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2*{second_account_pin_number}*{second_account_pin_number}*{second_account_given_name}*{second_account_family_name}*2"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '27'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Weka eneo lako\n0. Nyuma"
|
||||
|
||||
- name: Enter location [first_account_location - first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_metadata_entry_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "1*{first_account_pin_number}*{first_account_pin_number}*{first_account_given_name}*{first_account_family_name}*1*{first_account_location}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '55'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Please enter a product or service you offer\n0. Back"
|
||||
|
||||
- name: Enter location [second_account_location - second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_metadata_entry_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2*{second_account_pin_number}*{second_account_pin_number}*{second_account_given_name}*{second_account_family_name}*2*{second_account_location}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '42'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Weka bidhaa ama huduma unauza\n0. Nyuma"
|
||||
|
||||
- name: Enter product [first_account_product - first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_metadata_entry_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "1*{first_account_pin_number}*{first_account_pin_number}*{first_account_given_name}*{first_account_family_name}*1*{first_account_location}*{first_account_product}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '51'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Balance {gift_value} {token_symbol}\n1. Send\n2. My Account\n3. Help"
|
||||
delay_before: 10
|
||||
|
||||
- name: Enter product [second_account_product - second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_metadata_entry_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2*{second_account_pin_number}*{second_account_pin_number}*{second_account_given_name}*{second_account_family_name}*2*{second_account_location}*{second_account_product}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '56'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Salio {gift_value} {token_symbol}\n1. Tuma\n2. Akaunti yangu\n3. Usaidizi"
|
||||
delay_before: 10
|
||||
@@ -0,0 +1,587 @@
|
||||
test_name: Test performing account management operations.
|
||||
marks:
|
||||
- usefixtures:
|
||||
- server_url
|
||||
- token_symbol
|
||||
- first_account_pin_number
|
||||
- second_account_pin_number
|
||||
- first_account_phone_number
|
||||
- second_account_phone_number
|
||||
- first_account_management_session_id
|
||||
- second_account_management_session_id
|
||||
- first_account_management_session_id_1
|
||||
- second_account_management_session_id_1
|
||||
- first_account_new_pin_number
|
||||
- second_account_new_pin_number
|
||||
- fourth
|
||||
|
||||
stages:
|
||||
- name: Account management start menu [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_account_management_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: ""
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '51'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Balance 58.00 {token_symbol}\n1. Send\n2. My Account\n3. Help"
|
||||
|
||||
- name: Account management start menu [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_account_management_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: ""
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '56'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Salio 42.00 {token_symbol}\n1. Tuma\n2. Akaunti yangu\n3. Usaidizi"
|
||||
|
||||
- name: Account management menu [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_account_management_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "2"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '105'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON My account\n1. My profile\n2. Change language\n3. Check balance\n4. Check statement\n5. Change PIN\n0. Back"
|
||||
|
||||
- name: Account management menu [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_account_management_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '148'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Akaunti yangu\n1. Wasifu wangu\n2. Chagua lugha utakayotumia\n3. Angalia salio\n4. Angalia taarifa ya matumizi\n5. Badilisha nambari ya siri\n0. Nyuma"
|
||||
|
||||
- name: Language change [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_account_management_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "2*2"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '51'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Choose language\n1. English\n2. Kiswahili\n0. Back"
|
||||
|
||||
- name: Language change [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_account_management_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2*2"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '51'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Chagua lugha\n1. Kingereza\n2. Kiswahili\n0. Nyuma"
|
||||
|
||||
- name: Select language [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_account_management_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "2*2*2"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '30'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "END Asante kwa kutumia huduma."
|
||||
|
||||
- name: Select language [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_account_management_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2*2*1"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '36'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "END Thank you for using the service."
|
||||
|
||||
- name: Second account management start menu [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_account_management_session_id_1}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: ""
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '56'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Salio 58.00 {token_symbol}\n1. Tuma\n2. Akaunti yangu\n3. Usaidizi"
|
||||
|
||||
- name: Second account management start menu [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_account_management_session_id_1}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: ""
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '51'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Balance 42.00 {token_symbol}\n1. Send\n2. My Account\n3. Help"
|
||||
|
||||
- name: Second account management menu [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_account_management_session_id_1}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "2"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '148'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Akaunti yangu\n1. Wasifu wangu\n2. Chagua lugha utakayotumia\n3. Angalia salio\n4. Angalia taarifa ya matumizi\n5. Badilisha nambari ya siri\n0. Nyuma"
|
||||
|
||||
- name: Second account management menu [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_account_management_session_id_1}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '105'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON My account\n1. My profile\n2. Change language\n3. Check balance\n4. Check statement\n5. Change PIN\n0. Back"
|
||||
|
||||
- name: Check balance [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_account_management_session_id_1}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "2*3"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '49'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Tafadhali weka PIN yako kuona salio.\n0. Nyuma"
|
||||
|
||||
- name: Check balance [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_account_management_session_id_1}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2*3"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '50'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Please enter your PIN to view balances\n0. Back"
|
||||
|
||||
- name: Display balances [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_account_management_session_id_1}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "2*3*{first_account_pin_number}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Salio zako ni zifuatazo:\n salio: 58.00 {token_symbol}\n ushuru: {token_symbol}\n tuzo: {token_symbol}\n0. Nyuma"
|
||||
|
||||
- name: Display balances [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_account_management_session_id_1}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2*3*{second_account_pin_number}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Your balances are as follows:\n balance: 42.00 {token_symbol}\n fees: {token_symbol}\n rewards: {token_symbol}\n0. Back"
|
||||
|
||||
- name: Resume account management menu [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_account_management_session_id_1}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "2*3*{first_account_pin_number}*0"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '148'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Akaunti yangu\n1. Wasifu wangu\n2. Chagua lugha utakayotumia\n3. Angalia salio\n4. Angalia taarifa ya matumizi\n5. Badilisha nambari ya siri\n0. Nyuma"
|
||||
|
||||
- name: Resume account management menu [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_account_management_session_id_1}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2*3*{second_account_pin_number}*0"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '105'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON My account\n1. My profile\n2. Change language\n3. Check balance\n4. Check statement\n5. Change PIN\n0. Back"
|
||||
|
||||
- name: Change pin number [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_account_management_session_id_1}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "2*3*{first_account_pin_number}*0*5"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '34'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Weka nambari ya siri.\n0. Nyuma"
|
||||
|
||||
- name: Change pin number [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_account_management_session_id_1}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2*3*{second_account_pin_number}*0*5"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '30'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Enter current PIN.\n0. Back"
|
||||
|
||||
- name: Enter old pin [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_account_management_session_id_1}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "2*3*{first_account_pin_number}*0*5*{first_account_pin_number}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '38'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Weka nambari ya siri mpya\n0. Nyuma"
|
||||
|
||||
- name: Enter old pin [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_account_management_session_id_1}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2*3*{second_account_pin_number}*0*5*{second_account_pin_number}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '42'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Enter your new four number PIN\n0. Back"
|
||||
|
||||
- name: Enter new pin [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_account_management_session_id_1}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "2*3*{first_account_pin_number}*0*5*{first_account_pin_number}*{first_account_new_pin_number}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '31'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Weka PIN yako tena\n0. Nyuma"
|
||||
|
||||
- name: Enter new pin [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_account_management_session_id_1}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2*3*{second_account_pin_number}*0*5*{second_account_pin_number}*{second_account_new_pin_number}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '48'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Enter your new four number PIN again\n0. Back"
|
||||
|
||||
- name: Enter new pin confirmation [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_account_management_session_id_1}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "2*3*{first_account_pin_number}*0*5*{first_account_pin_number}*{first_account_new_pin_number}*{first_account_new_pin_number}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '91'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Ombi lako limetumwa. Utapokea uthibitishaji wa SMS kwa muda mfupi.\n00. Nyuma\n99. Ondoka"
|
||||
|
||||
- name: Enter new pin confirmation [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_account_management_session_id_1}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "2*3*{second_account_pin_number}*0*5*{second_account_pin_number}*{second_account_new_pin_number}*{second_account_new_pin_number}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '82'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Your request has been sent. You will receive an SMS shortly.\n00. Back\n99. Exit"
|
||||
1573
apps/cic-ussd/tests/integration/test_profile_management.tavern.yaml
Normal file
1573
apps/cic-ussd/tests/integration/test_profile_management.tavern.yaml
Normal file
File diff suppressed because it is too large
Load Diff
282
apps/cic-ussd/tests/integration/test_transactions.tavern.yaml
Normal file
282
apps/cic-ussd/tests/integration/test_transactions.tavern.yaml
Normal file
@@ -0,0 +1,282 @@
|
||||
test_name: Test that the two test accounts can trade with each other.
|
||||
marks:
|
||||
- usefixtures:
|
||||
- gift_value
|
||||
- server_url
|
||||
- token_symbol
|
||||
- first_account_family_name
|
||||
- second_account_family_name
|
||||
- first_account_given_name
|
||||
- second_account_given_name
|
||||
- first_account_phone_number
|
||||
- second_account_phone_number
|
||||
- first_account_pin_number
|
||||
- second_account_pin_number
|
||||
- first_transaction_session_id
|
||||
- second_transaction_session_id
|
||||
- first_account_verify_balance_session_id
|
||||
- second_account_verify_balance_session_id
|
||||
- second
|
||||
|
||||
stages:
|
||||
- name: Transactions start menu [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_transaction_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: ""
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '51'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Balance {gift_value} {token_symbol}\n1. Send\n2. My Account\n3. Help"
|
||||
|
||||
- name: Transactions start menu [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_transaction_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: ""
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '56'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Salio {gift_value} {token_symbol}\n1. Tuma\n2. Akaunti yangu\n3. Usaidizi"
|
||||
|
||||
- name: Initate transcation attempt [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_transaction_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "1"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '30'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Enter phone number\n0. Back"
|
||||
|
||||
- name: Initate transcation attempt [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_transaction_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "1"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '33'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Weka nambari ya simu\n0. Nyuma"
|
||||
|
||||
- name: Enter phone number [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_transaction_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "1*{second_account_phone_number}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '24'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Enter amount\n0. Back"
|
||||
|
||||
- name: Enter phone number [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_transaction_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "1*{first_account_phone_number}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '25'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Weka kiwango\n0. Nyuma"
|
||||
|
||||
- name: Enter transcation amount [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_transaction_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "1*{second_account_phone_number}*17"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON {second_account_given_name} {second_account_family_name} {second_account_phone_number} will receive 17.00 {token_symbol} from {first_account_given_name} {first_account_family_name} {first_account_phone_number}.\nPlease enter your PIN to confirm.\n0. Back"
|
||||
|
||||
- name: Enter transcation amount [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_transaction_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "1*{first_account_phone_number}*25"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON {first_account_given_name} {first_account_family_name} {first_account_phone_number} atapokea 25.00 {token_symbol} kutoka kwa {second_account_given_name} {second_account_family_name} {second_account_phone_number}.\nTafadhali weka nambari yako ya siri kudhibitisha.\n0. Nyuma"
|
||||
|
||||
- name: Pin to authorize transaction [first account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_transaction_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: "1*{second_account_phone_number}*17*{first_account_pin_number}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Your request has been sent. {second_account_given_name} {second_account_family_name} {second_account_phone_number} will receive 17.00 {token_symbol} from {first_account_given_name} {first_account_family_name} {first_account_phone_number}.\n00. Back\n99. Exit"
|
||||
|
||||
- name: Pin to authorize transaction [second account]
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_transaction_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: "1*{first_account_phone_number}*25*{second_account_pin_number}"
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Ombi lako limetumwa. {first_account_given_name} {first_account_family_name} {first_account_phone_number} atapokea 25.00 {token_symbol} kutoka kwa {second_account_given_name} {second_account_family_name} {second_account_phone_number}.\n00. Nyuma\n99. Ondoka"
|
||||
|
||||
- name: Verify balance changes [first account]
|
||||
delay_before: 10
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{first_account_verify_balance_session_id}"
|
||||
phoneNumber: "{first_account_phone_number}"
|
||||
text: ""
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '51'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Balance 58.00 {token_symbol}\n1. Send\n2. My Account\n3. Help"
|
||||
|
||||
- name: Verify balance changes [second account]
|
||||
delay_before: 10
|
||||
request:
|
||||
url: "{server_url}"
|
||||
data:
|
||||
serviceCode: "*483*46#"
|
||||
sessionId: "{second_account_verify_balance_session_id}"
|
||||
phoneNumber: "{second_account_phone_number}"
|
||||
text: ""
|
||||
headers:
|
||||
content-type: "application/x-www-form-urlencoded"
|
||||
method: POST
|
||||
response:
|
||||
status_code:
|
||||
- 200
|
||||
headers:
|
||||
Content-Length: '56'
|
||||
Content-Type: "text/plain"
|
||||
verify_response_with:
|
||||
function: ext.validator:validate_response
|
||||
extra_kwargs:
|
||||
expected_response: "CON Salio 42.00 {token_symbol}\n1. Tuma\n2. Akaunti yangu\n3. Usaidizi"
|
||||
@@ -4,12 +4,13 @@
|
||||
"source": "enter_gender",
|
||||
"dest": "enter_location",
|
||||
"after": "cic_ussd.state_machine.logic.user.save_metadata_attribute_to_session_data",
|
||||
"conditions": "cic_ussd.state_machine.logic.validator.is_valid_gender_selection"
|
||||
"conditions": "cic_ussd.state_machine.logic.validator.is_valid_gender_selection",
|
||||
"unless": "cic_ussd.state_machine.logic.validator.has_cached_user_metadata"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "enter_gender",
|
||||
"dest": "standard_pin_authorization",
|
||||
"dest": "gender_edit_pin_authorization",
|
||||
"after": "cic_ussd.state_machine.logic.user.save_metadata_attribute_to_session_data",
|
||||
"conditions": [
|
||||
"cic_ussd.state_machine.logic.validator.has_cached_user_metadata",
|
||||
@@ -18,15 +19,14 @@
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "standard_pin_authorization",
|
||||
"source": "gender_edit_pin_authorization",
|
||||
"dest": "exit",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_authorized_pin",
|
||||
"after": "cic_ussd.state_machine.logic.user.edit_user_metadata_attribute",
|
||||
"unless": "cic_ussd.state_machine.logic.validator.has_cached_user_metadata"
|
||||
"after": "cic_ussd.state_machine.logic.user.edit_user_metadata_attribute"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "standard_pin_authorization",
|
||||
"source": "gender_edit_pin_authorization",
|
||||
"dest": "exit_pin_blocked",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_locked_account"
|
||||
},
|
||||
|
||||
@@ -3,26 +3,26 @@
|
||||
"trigger": "scan_data",
|
||||
"source": "enter_location",
|
||||
"dest": "enter_products",
|
||||
"after": "cic_ussd.state_machine.logic.user.save_metadata_attribute_to_session_data"
|
||||
"after": "cic_ussd.state_machine.logic.user.save_metadata_attribute_to_session_data",
|
||||
"unless": "cic_ussd.state_machine.logic.validator.has_cached_user_metadata"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "enter_location",
|
||||
"dest": "standard_pin_authorization",
|
||||
"dest": "location_edit_pin_authorization",
|
||||
"after": "cic_ussd.state_machine.logic.user.save_metadata_attribute_to_session_data",
|
||||
"conditions": "cic_ussd.state_machine.logic.validator.has_cached_user_metadata"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "standard_pin_authorization",
|
||||
"source": "location_edit_pin_authorization",
|
||||
"dest": "exit",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_authorized_pin",
|
||||
"after": "cic_ussd.state_machine.logic.user.edit_user_metadata_attribute",
|
||||
"unless": "cic_ussd.state_machine.logic.validator.has_cached_user_metadata"
|
||||
"after": "cic_ussd.state_machine.logic.user.edit_user_metadata_attribute"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "standard_pin_authorization",
|
||||
"source": "location_edit_pin_authorization",
|
||||
"dest": "exit_pin_blocked",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_locked_account"
|
||||
}
|
||||
|
||||
@@ -7,49 +7,28 @@
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "enter_given_name",
|
||||
"dest": "standard_pin_authorization",
|
||||
"source": "enter_family_name",
|
||||
"dest": "name_edit_pin_authorization",
|
||||
"after": "cic_ussd.state_machine.logic.user.save_metadata_attribute_to_session_data",
|
||||
"conditions": "cic_ussd.state_machine.logic.validator.has_cached_user_metadata"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "standard_pin_authorization",
|
||||
"dest": "exit",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_authorized_pin",
|
||||
"after": "cic_ussd.state_machine.logic.user.edit_user_metadata_attribute",
|
||||
"unless": "cic_ussd.state_machine.logic.validator.has_cached_user_metadata"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "standard_pin_authorization",
|
||||
"dest": "exit_pin_blocked",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_locked_account"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "enter_family_name",
|
||||
"dest": "enter_gender",
|
||||
"after": "cic_ussd.state_machine.logic.user.save_metadata_attribute_to_session_data"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "enter_family_name",
|
||||
"dest": "standard_pin_authorization",
|
||||
"after": "cic_ussd.state_machine.logic.user.save_metadata_attribute_to_session_data",
|
||||
"conditions": "cic_ussd.state_machine.logic.validator.has_cached_user_metadata"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "standard_pin_authorization",
|
||||
"dest": "exit",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_authorized_pin",
|
||||
"after": "cic_ussd.state_machine.logic.user.edit_user_metadata_attribute",
|
||||
"unless": "cic_ussd.state_machine.logic.validator.has_cached_user_metadata"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "standard_pin_authorization",
|
||||
"source": "name_edit_pin_authorization",
|
||||
"dest": "exit",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_authorized_pin",
|
||||
"after": "cic_ussd.state_machine.logic.user.edit_user_metadata_attribute"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "name_edit_pin_authorization",
|
||||
"dest": "exit_pin_blocked",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_locked_account"
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
"trigger": "scan_data",
|
||||
"source": "enter_current_pin",
|
||||
"dest": "exit_pin_blocked",
|
||||
"conditions": "cic_ussd.state_machine.logic.menu.is_blocked_pin"
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_blocked_pin"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "enter_new_pin",
|
||||
"dest": "new_pin_confirmation",
|
||||
"after": "cic_ussd.state_machine.logic.pin.save_initial_pin_to_session_data",
|
||||
"conditions": "cic_ussd.state_machine.logic.menu.is_valid_new_pin"
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_valid_new_pin"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
@@ -28,7 +28,7 @@
|
||||
"source": "new_pin_confirmation",
|
||||
"dest": "complete",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.pins_match",
|
||||
"after": "cic_ussd.state_machine.logic.menu.complete_pin_change"
|
||||
"after": "cic_ussd.state_machine.logic.pin.complete_pin_change"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "enter_products",
|
||||
"dest": "standard_pin_authorization",
|
||||
"dest": "products_edit_pin_authorization",
|
||||
"conditions": "cic_ussd.state_machine.logic.validator.has_cached_user_metadata",
|
||||
"after": "cic_ussd.state_machine.logic.user.save_metadata_attribute_to_session_data"
|
||||
},
|
||||
@@ -13,18 +13,19 @@
|
||||
"after": [
|
||||
"cic_ussd.state_machine.logic.user.save_metadata_attribute_to_session_data",
|
||||
"cic_ussd.state_machine.logic.user.save_complete_user_metadata"
|
||||
]
|
||||
],
|
||||
"unless": "cic_ussd.state_machine.logic.validator.has_cached_user_metadata"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "standard_pin_authorization",
|
||||
"source": "products_edit_pin_authorization",
|
||||
"dest": "exit",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_authorized_pin",
|
||||
"after": "cic_ussd.state_machine.logic.user.edit_user_metadata_attribute"
|
||||
},
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "standard_pin_authorization",
|
||||
"source": "products_edit_pin_authorization",
|
||||
"dest": "exit_pin_blocked",
|
||||
"conditions": "cic_ussd.state_machine.logic.pin.is_locked_account"
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{
|
||||
"trigger": "scan_data",
|
||||
"source": "metadata_management",
|
||||
"dest": "enter_age",
|
||||
"dest": "enter_gender",
|
||||
"conditions": "cic_ussd.state_machine.logic.menu.menu_two_selected"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ en:
|
||||
2. Kiswahili
|
||||
3. Help
|
||||
initial_pin_entry: |-
|
||||
CON Please enter a new four number PIN for your account.
|
||||
CON Please enter a new four number PIN for your account.
|
||||
0. Back
|
||||
initial_pin_confirmation: |-
|
||||
CON Enter your four number PIN again
|
||||
@@ -76,7 +76,10 @@ en:
|
||||
CON Enter current PIN. You have %{remaining_attempts} attempts remaining.
|
||||
0. Back
|
||||
enter_new_pin: |-
|
||||
CON Enter new PIN again
|
||||
CON Enter your new four number PIN
|
||||
0. Back
|
||||
new_pin_confirmation: |-
|
||||
CON Enter your new four number PIN again
|
||||
0. Back
|
||||
transaction_pin_authorization:
|
||||
first: |-
|
||||
@@ -107,6 +110,34 @@ en:
|
||||
retry: |-
|
||||
CON Please enter your PIN. You have %{remaining_attempts} attempts remaining
|
||||
0. Back
|
||||
name_edit_pin_authorization:
|
||||
first: |-
|
||||
CON Please enter your PIN
|
||||
0. Back
|
||||
retry: |-
|
||||
CON Please enter your PIN. You have %{remaining_attempts} attempts remaining
|
||||
0. Back
|
||||
gender_edit_pin_authorization:
|
||||
first: |-
|
||||
CON Please enter your PIN
|
||||
0. Back
|
||||
retry: |-
|
||||
CON Please enter your PIN. You have %{remaining_attempts} attempts remaining
|
||||
0. Back
|
||||
location_edit_pin_authorization:
|
||||
first: |-
|
||||
CON Please enter your PIN
|
||||
0. Back
|
||||
retry: |-
|
||||
CON Please enter your PIN. You have %{remaining_attempts} attempts remaining
|
||||
0. Back
|
||||
products_edit_pin_authorization:
|
||||
first: |-
|
||||
CON Please enter your PIN
|
||||
0. Back
|
||||
retry: |-
|
||||
CON Please enter your PIN. You have %{remaining_attempts} attempts remaining
|
||||
0. Back
|
||||
account_balances: |-
|
||||
CON Your balances are as follows:
|
||||
balance: %{operational_balance} %{token_symbol}
|
||||
|
||||
@@ -21,7 +21,7 @@ sw:
|
||||
CON Weka jinsia yako
|
||||
1. Mwanaume
|
||||
2. Mwanamke
|
||||
3. Nyngine
|
||||
3. Nyngine
|
||||
0. Nyuma
|
||||
enter_location: |-
|
||||
CON Weka eneo lako
|
||||
@@ -61,7 +61,7 @@ sw:
|
||||
Jina: %{full_name}
|
||||
Jinsia: %{gender}
|
||||
Eneo: %{location}
|
||||
Unauza: %{user_bio}
|
||||
Unauza: %{products}
|
||||
0. Nyuma
|
||||
select_preferred_language: |-
|
||||
CON Chagua lugha
|
||||
@@ -78,6 +78,9 @@ sw:
|
||||
enter_new_pin: |-
|
||||
CON Weka nambari ya siri mpya
|
||||
0. Nyuma
|
||||
new_pin_confirmation: |-
|
||||
CON Weka PIN yako tena
|
||||
0. Nyuma
|
||||
transaction_pin_authorization:
|
||||
first: |-
|
||||
CON %{recipient_information} atapokea %{transaction_amount} %{token_symbol} kutoka kwa %{sender_information}.
|
||||
@@ -86,9 +89,9 @@ sw:
|
||||
retry: |-
|
||||
CON Weka nambari ya siri. Una majaribio %{remaining_attempts} yaliyobaki.
|
||||
0. Nyuma
|
||||
standard_pin_authorization:
|
||||
display_metadata_pin_authorization:
|
||||
first: |-
|
||||
CON Tafadhali weka PIN yako.
|
||||
CON Tafadhali weka PIN yako
|
||||
0. Nyuma
|
||||
retry: |-
|
||||
CON Tafadhali weka PIN yako. Una majaribio %{remaining_attempts} yaliyobaki.
|
||||
@@ -107,12 +110,40 @@ sw:
|
||||
retry: |-
|
||||
CON Tafadhali weka PIN yako. Una majaribio %{remaining_attempts} yaliyobaki.
|
||||
0. Nyuma
|
||||
name_edit_pin_authorization:
|
||||
first: |-
|
||||
CON Tafadhali weka PIN yako
|
||||
0. Nyuma
|
||||
retry: |-
|
||||
CON Tafadhali weka PIN yako. Una majaribio %{remaining_attempts} yaliyobaki.
|
||||
0. Nyuma
|
||||
gender_edit_pin_authorization:
|
||||
first: |-
|
||||
CON Tafadhali weka PIN yako
|
||||
0. Nyuma
|
||||
retry: |-
|
||||
CON Tafadhali weka PIN yako. Una majaribio %{remaining_attempts} yaliyobaki.
|
||||
0. Nyuma
|
||||
location_edit_pin_authorization:
|
||||
first: |-
|
||||
CON Tafadhali weka PIN yako
|
||||
0. Nyuma
|
||||
retry: |-
|
||||
CON Tafadhali weka PIN yako. Una majaribio %{remaining_attempts} yaliyobaki.
|
||||
0. Nyuma
|
||||
products_edit_pin_authorization:
|
||||
first: |-
|
||||
CON Tafadhali weka PIN yako
|
||||
0. Nyuma
|
||||
retry: |-
|
||||
CON Tafadhali weka PIN yako. Una majaribio %{remaining_attempts} yaliyobaki.
|
||||
0. Nyuma
|
||||
account_balances: |-
|
||||
CON Salio zako ni zifuatazo:
|
||||
salio: %{operational_balance}
|
||||
ushuru: %{tax}
|
||||
tuzo: %{bonus}
|
||||
0. Back
|
||||
salio: %{operational_balance} %{token_symbol}
|
||||
ushuru: %{tax} %{token_symbol}
|
||||
tuzo: %{bonus} %{token_symbol}
|
||||
0. Nyuma
|
||||
first_transaction_set: |-
|
||||
CON %{first_transaction_set}
|
||||
1. Mbele
|
||||
|
||||
@@ -87,6 +87,7 @@ COPY contract-migration/testdata/pgp testdata/pgp
|
||||
COPY contract-migration/sarafu_declaration.json sarafu_declaration.json
|
||||
COPY contract-migration/keystore keystore
|
||||
COPY contract-migration/envlist .
|
||||
COPY contract-migration/scripts scripts/
|
||||
|
||||
# A shared output dir for environment configs
|
||||
RUN mkdir -p /tmp/cic/config
|
||||
|
||||
@@ -89,7 +89,12 @@ After this step is run, you can find top-level ethereum addresses (like the cic
|
||||
|
||||
|
||||
#### Custodial provisions
|
||||
|
||||
response_data = send_ussd_request(address, self.data_dir)
|
||||
state = response_data[:3]
|
||||
out = response_data[4:]
|
||||
m = '{} {}'.format(state, out[:7])
|
||||
if m != 'CON Welcome':
|
||||
raise VerifierError(response_data, 'ussd')
|
||||
This step is _only_ needed if you are importing using `cic_eth` or `cic_ussd`
|
||||
|
||||
`RUN_MASK=2 docker-compose up contract-migration`
|
||||
@@ -123,16 +128,12 @@ The keystore file used for transferring external opening balances tracker is rel
|
||||
|
||||
All external balance transactions are saved in raw wire format in `<datadir>/txs`, with transaction hash as file name.
|
||||
|
||||
If the contract migrations have been executed with the default "giftable" token contract, then the `token_symbol` in the `import_balance` scripts should be set to `GFT`.
|
||||
|
||||
|
||||
|
||||
#### Alternative 1 - Sovereign wallet import - `eth`
|
||||
|
||||
|
||||
First, make a note of the **block height** before running anything:
|
||||
|
||||
`eth-info -p http://localhost:63545`
|
||||
First, make a note of the **block height** before running anything.
|
||||
|
||||
To import, run to _completion_:
|
||||
|
||||
@@ -142,7 +143,7 @@ After the script completes, keystore files for all generated accouts will be fou
|
||||
|
||||
Then run:
|
||||
|
||||
`python eth/import_balance.py -v -c config -r <cic_registry_address> -p <eth_provider> --token-symbol <token_symbol> --offset <block_height_at_start> -y ../keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c <datadir>`
|
||||
`python eth/import_balance.py -v -c config -r <cic_registry_address> -p <eth_provider> --offset <block_height_at_start> -y ../keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c <datadir>`
|
||||
|
||||
|
||||
|
||||
@@ -150,7 +151,7 @@ Then run:
|
||||
|
||||
Run in sequence, in first terminal:
|
||||
|
||||
`python cic_eth/import_balance.py -v -c config -p <eth_provider> -r <cic_registry_address> --token-symbol <token_symbol> -y ../keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c --head out`
|
||||
`python cic_eth/import_balance.py -v -c config -p <eth_provider> -r <cic_registry_address> -y ../keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c --head out`
|
||||
|
||||
In another terminal:
|
||||
|
||||
@@ -167,40 +168,14 @@ If you have previously run the `cic_ussd` import incompletely, it could be a goo
|
||||
|
||||
Then, in sequence, run in first terminal:
|
||||
|
||||
`python cic_eth/import_balance.py -v -c config -p <eth_provider> -r <cic_registry_address> --token-symbol <token_symbol> -y ../keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c out`
|
||||
`python cic_eth/import_balance.py -v -c config -p <eth_provider> -r <cic_registry_address> -y ../keystore/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c out`
|
||||
|
||||
In second terminal:
|
||||
|
||||
`python cic_ussd/import_users.py -v -c config out`
|
||||
|
||||
|
||||
|
||||
### Step 4 - Metadata import (optional)
|
||||
|
||||
The metadata import scripts can be run at any time after step 1 has been completed.
|
||||
|
||||
|
||||
#### Importing user metadata
|
||||
|
||||
To import the main user metadata structs, run:
|
||||
|
||||
`node cic_meta/import_meta.js <datadir> <number_of_users>`
|
||||
|
||||
Monitors a folder for output from the `import_users.py` script, adding the metadata found to the `cic-meta` service.
|
||||
|
||||
If _number of users_ is omitted the script will run until manually interrupted.
|
||||
|
||||
|
||||
|
||||
#### Importing phone pointer
|
||||
|
||||
`node cic_meta/import_meta_phone.js <datadir> <number_of_users>`
|
||||
|
||||
If you imported using `cic_ussd`, the phone pointer is _already added_ and this script will do nothing.
|
||||
|
||||
|
||||
##### Importing pins and ussd data (optional)
|
||||
|
||||
Once the user imports are complete the next step should be importing the user's pins and auxiliary ussd data. This can be done in 3 steps:
|
||||
|
||||
In one terminal run:
|
||||
@@ -224,6 +199,29 @@ The balance script is a celery task worker, and will not exit by itself in its c
|
||||
The connection parameters for the `cic-ussd-server` is currently _hardcoded_ in the `import_users.py` script file.
|
||||
|
||||
|
||||
### Step 4 - Metadata import (optional)
|
||||
|
||||
The metadata import scripts can be run at any time after step 1 has been completed.
|
||||
|
||||
|
||||
#### Importing user metadata
|
||||
|
||||
To import the main user metadata structs, run:
|
||||
|
||||
`node cic_meta/import_meta.js <datadir> <number_of_users>`
|
||||
|
||||
Monitors a folder for output from the `import_users.py` script, adding the metadata found to the `cic-meta` service.
|
||||
|
||||
If _number of users_ is omitted the script will run until manually interrupted.
|
||||
|
||||
|
||||
#### Importing phone pointer
|
||||
|
||||
`node cic_meta/import_meta_phone.js <datadir> <number_of_users>`
|
||||
|
||||
If you imported using `cic_ussd`, the phone pointer is _already added_ and this script will do nothing.
|
||||
|
||||
|
||||
### Step 5 - Verify
|
||||
|
||||
`python verify.py -v -c config -r <cic_registry_address> -p <eth_provider> <datadir>`
|
||||
@@ -1,76 +0,0 @@
|
||||
# external imports
|
||||
from chainlib.jsonrpc import JSONRPCException
|
||||
from eth_erc20 import ERC20
|
||||
from eth_accounts_index import AccountsIndex
|
||||
from eth_token_index import TokenUniqueSymbolIndex
|
||||
|
||||
class ERC20Token:
|
||||
|
||||
def __init__(self, chain_spec, address, conn):
|
||||
self.__address = address
|
||||
|
||||
c = ERC20(chain_spec)
|
||||
o = c.symbol(address)
|
||||
r = conn.do(o)
|
||||
self.__symbol = c.parse_symbol(r)
|
||||
|
||||
o = c.decimals(address)
|
||||
r = conn.do(o)
|
||||
self.__decimals = c.parse_decimals(r)
|
||||
|
||||
|
||||
def symbol(self):
|
||||
return self.__symbol
|
||||
|
||||
|
||||
def decimals(self):
|
||||
return self.__decimals
|
||||
|
||||
|
||||
class IndexCache:
|
||||
|
||||
def __init__(self, chain_spec, address):
|
||||
self.address = address
|
||||
self.chain_spec = chain_spec
|
||||
|
||||
|
||||
def parse(self, r):
|
||||
return r
|
||||
|
||||
|
||||
def get(self, conn):
|
||||
entries = []
|
||||
i = 0
|
||||
while True:
|
||||
o = self.o.entry(self.address, i)
|
||||
try:
|
||||
r = conn.do(o)
|
||||
entries.append(self.parse(r, conn))
|
||||
except JSONRPCException:
|
||||
return entries
|
||||
i += 1
|
||||
|
||||
|
||||
class AccountRegistryCache(IndexCache):
|
||||
|
||||
def __init__(self, chain_spec, address):
|
||||
super(AccountRegistryCache, self).__init__(chain_spec, address)
|
||||
self.o = AccountsIndex(chain_spec)
|
||||
self.get_accounts = self.get
|
||||
|
||||
|
||||
def parse(self, r, conn):
|
||||
return self.o.parse_account(r)
|
||||
|
||||
|
||||
class TokenRegistryCache(IndexCache):
|
||||
|
||||
def __init__(self, chain_spec, address):
|
||||
super(TokenRegistryCache, self).__init__(chain_spec, address)
|
||||
self.o = TokenUniqueSymbolIndex(chain_spec)
|
||||
self.get_tokens = self.get
|
||||
|
||||
|
||||
def parse(self, r, conn):
|
||||
token_address = self.o.parse_entry(r)
|
||||
return ERC20Token(self.chain_spec, token_address, conn)
|
||||
@@ -163,9 +163,9 @@ class TrafficProvisioner:
|
||||
"""Aux parameter template to be passed to the traffic generator module"""
|
||||
|
||||
|
||||
def __init__(self, conn):
|
||||
self.tokens = self.oracles['token'].get_tokens(conn)
|
||||
self.accounts = self.oracles['account'].get_accounts(conn)
|
||||
def __init__(self):
|
||||
self.tokens = self.oracles['token'].get_tokens()
|
||||
self.accounts = self.oracles['account'].get_accounts()
|
||||
self.aux = copy.copy(self.default_aux)
|
||||
self.__balances = {}
|
||||
for a in self.accounts:
|
||||
@@ -277,14 +277,13 @@ class TrafficSyncHandler:
|
||||
:type traffic_router: TrafficRouter
|
||||
:raises Exception: Any Exception redis may raise on connection attempt.
|
||||
"""
|
||||
def __init__(self, config, traffic_router, conn):
|
||||
def __init__(self, config, traffic_router):
|
||||
self.traffic_router = traffic_router
|
||||
self.redis_channel = str(uuid.uuid4())
|
||||
self.pubsub = self.__connect_redis(self.redis_channel, config)
|
||||
self.traffic_items = {}
|
||||
self.config = config
|
||||
self.init = False
|
||||
self.conn = conn
|
||||
|
||||
|
||||
# connects to redis
|
||||
@@ -308,7 +307,7 @@ class TrafficSyncHandler:
|
||||
:param tx_index: Syncer block transaction index at time of call.
|
||||
:type tx_index: number
|
||||
"""
|
||||
traffic_provisioner = TrafficProvisioner(self.conn)
|
||||
traffic_provisioner = TrafficProvisioner()
|
||||
traffic_provisioner.add_aux('redis_channel', self.redis_channel)
|
||||
|
||||
refresh_accounts = None
|
||||
@@ -344,7 +343,7 @@ class TrafficSyncHandler:
|
||||
sender = traffic_provisioner.accounts[sender_index]
|
||||
#balance_full = balances[sender][token_pair[0].symbol()]
|
||||
if len(sender_indices) == 1:
|
||||
sender_indices[sender_index] = sender_indices[len(sender_indices)-1]
|
||||
sender_indices[m] = sender_sender_indices[len(senders)-1]
|
||||
sender_indices = sender_indices[:len(sender_indices)-1]
|
||||
|
||||
balance_full = traffic_provisioner.balance(sender, token_pair[0])
|
||||
@@ -352,14 +351,7 @@ class TrafficSyncHandler:
|
||||
recipient_index = random.randint(0, len(traffic_provisioner.accounts)-1)
|
||||
recipient = traffic_provisioner.accounts[recipient_index]
|
||||
|
||||
logg.debug('trigger item {} tokens {} sender {} recipient {} balance {}'.format(
|
||||
traffic_item,
|
||||
token_pair,
|
||||
sender,
|
||||
recipient,
|
||||
balance_full,
|
||||
)
|
||||
)
|
||||
logg.debug('trigger item {} tokens {} sender {} recipient {} balance {}')
|
||||
(e, t, balance_result,) = traffic_item.method(
|
||||
token_pair,
|
||||
sender,
|
||||
@@ -367,6 +359,7 @@ class TrafficSyncHandler:
|
||||
balance_full,
|
||||
traffic_provisioner.aux,
|
||||
block_number,
|
||||
tx_index,
|
||||
)
|
||||
traffic_provisioner.update_balance(sender, token_pair[0], balance_result)
|
||||
sender_indices.append(recipient_index)
|
||||
@@ -3,7 +3,7 @@ import logging
|
||||
import copy
|
||||
|
||||
# external imports
|
||||
from cic_registry.registry import Registry
|
||||
from cic_registry import CICRegistry
|
||||
from eth_token_index import TokenUniqueSymbolIndex
|
||||
from eth_accounts_index import AccountRegistry
|
||||
from chainlib.chain import ChainSpec
|
||||
@@ -3,7 +3,7 @@ import logging
|
||||
|
||||
# external imports
|
||||
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
|
||||
from crypto_dev_signer.keystore.dict import DictKeystore
|
||||
from crypto_dev_signer.keystore import DictKeystore
|
||||
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
@@ -11,7 +11,7 @@ queue = 'cic-eth'
|
||||
name = 'account'
|
||||
|
||||
|
||||
def do(token_pair, sender, recipient, sender_balance, aux, block_number):
|
||||
def do(token_pair, sender, recipient, sender_balance, aux, block_number, tx_index):
|
||||
"""Triggers creation and registration of new account through the custodial cic-eth component.
|
||||
|
||||
It expects the following aux parameters to exist:
|
||||
@@ -5,7 +5,7 @@ logging.basicConfig(level=logging.WARNING)
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
def do(token_pair, sender, recipient, sender_balance, aux, block_number):
|
||||
def do(token_pair, sender, recipient, sender_balance, aux, block_number, tx_index):
|
||||
"""Defines the function signature for a traffic generator. The method itself only logs the input parameters.
|
||||
|
||||
If the error position in the return tuple is not None, the calling code should consider the generation as failed, and not count it towards the limit of simultaneous traffic items that can be simultaneously in flight.
|
||||
@@ -26,10 +26,12 @@ def do(token_pair, sender, recipient, sender_balance, aux, block_number):
|
||||
:type aux: dict
|
||||
:param block_number: Syncer block number position at time of method call
|
||||
:type block_number: number
|
||||
:param tx_index: Syncer block transaction index position at time of method call
|
||||
:type tx_index: number
|
||||
:raises KeyError: Missing required aux element
|
||||
:returns: Exception|None, task_id|None and adjusted_sender_balance respectively
|
||||
:rtype: tuple
|
||||
"""
|
||||
logg.debug('running {} {} {} {} {} {} {}'.format(__name__, token_pair, sender, recipient, sender_balance, aux, block_number))
|
||||
logg.debug('running {} {} {} {} {} {} {} {}'.format(__name__, token_pair, sender, recipient, sender_balance, aux, block_number, tx_index))
|
||||
|
||||
return (None, None, sender_balance, )
|
||||
@@ -12,7 +12,7 @@ queue = 'cic-eth'
|
||||
name = 'erc20_transfer'
|
||||
|
||||
|
||||
def do(token_pair, sender, recipient, sender_balance, aux, block_number):
|
||||
def do(token_pair, sender, recipient, sender_balance, aux, block_number, tx_index):
|
||||
"""Triggers an ERC20 token transfer through the custodial cic-eth component, with a randomly chosen amount in integer resolution.
|
||||
|
||||
It expects the following aux parameters to exist:
|
||||
@@ -33,7 +33,7 @@ def do(token_pair, sender, recipient, sender_balance, aux, block_number):
|
||||
balance_units = int(sender_balance_value / decimals)
|
||||
|
||||
if balance_units <= 0:
|
||||
return (AttributeError('sender {} has zero balance ({} / {})'.format(sender, sender_balance_value, decimals)), None, 0,)
|
||||
return (AttributeError('sender {} has zero balance'), None, 0,)
|
||||
|
||||
spend_units = random.randint(1, balance_units)
|
||||
spend_value = spend_units * decimals
|
||||
101
apps/contract-migration/scripts/cic_eth/traffic/traffic.py
Normal file
101
apps/contract-migration/scripts/cic_eth/traffic/traffic.py
Normal file
@@ -0,0 +1,101 @@
|
||||
# standard imports
|
||||
import os
|
||||
import logging
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
|
||||
# external imports
|
||||
import redis
|
||||
import celery
|
||||
from chainsyncer.backend import MemBackend
|
||||
from chainsyncer.driver import HeadSyncer
|
||||
from chainlib.eth.connection import HTTPConnection
|
||||
from chainlib.eth.gas import DefaultGasOracle
|
||||
from chainlib.eth.nonce import DefaultNonceOracle
|
||||
from chainlib.eth.block import block_latest
|
||||
from hexathon import strip_0x
|
||||
|
||||
# local imports
|
||||
import common
|
||||
from cmd.traffic import (
|
||||
TrafficItem,
|
||||
TrafficRouter,
|
||||
TrafficProvisioner,
|
||||
TrafficSyncHandler,
|
||||
)
|
||||
from cmd.traffic import add_args as add_traffic_args
|
||||
|
||||
|
||||
# common basics
|
||||
script_dir = os.path.realpath(os.path.dirname(__file__))
|
||||
logg = common.log.create()
|
||||
argparser = common.argparse.create(script_dir, common.argparse.full_template)
|
||||
argparser = common.argparse.add(argparser, add_traffic_args, 'traffic')
|
||||
args = common.argparse.parse(argparser, logg)
|
||||
config = common.config.create(args.c, args, args.env_prefix)
|
||||
|
||||
# map custom args to local config entries
|
||||
batchsize = args.batch_size
|
||||
if batchsize < 1:
|
||||
batchsize = 1
|
||||
logg.info('batch size {}'.format(batchsize))
|
||||
config.add(batchsize, '_BATCH_SIZE', True)
|
||||
|
||||
config.add(args.redis_host_callback, '_REDIS_HOST_CALLBACK', True)
|
||||
config.add(args.redis_port_callback, '_REDIS_PORT_CALLBACK', True)
|
||||
|
||||
config.add(args.y, '_KEYSTORE_FILE', True)
|
||||
|
||||
config.add(args.q, '_CELERY_QUEUE', True)
|
||||
|
||||
common.config.log(config)
|
||||
|
||||
|
||||
def main():
|
||||
# create signer (not currently in use, but needs to be accessible for custom traffic item generators)
|
||||
(signer_address, signer) = common.signer.from_keystore(config.get('_KEYSTORE_FILE'))
|
||||
|
||||
# connect to celery
|
||||
celery.Celery(broker=config.get('CELERY_BROKER_URL'), backend=config.get('CELERY_RESULT_URL'))
|
||||
|
||||
# set up registry
|
||||
w3 = common.rpc.create(config.get('ETH_PROVIDER')) # replace with HTTPConnection when registry has been so refactored
|
||||
registry = common.registry.init_legacy(config, w3)
|
||||
|
||||
# Connect to blockchain with chainlib
|
||||
conn = HTTPConnection(config.get('ETH_PROVIDER'))
|
||||
gas_oracle = DefaultGasOracle(conn)
|
||||
nonce_oracle = DefaultNonceOracle(signer_address, conn)
|
||||
|
||||
# Set up magic traffic handler
|
||||
traffic_router = TrafficRouter()
|
||||
traffic_router.apply_import_dict(config.all(), config)
|
||||
handler = TrafficSyncHandler(config, traffic_router)
|
||||
|
||||
# Set up syncer
|
||||
syncer_backend = MemBackend(config.get('CIC_CHAIN_SPEC'), 0)
|
||||
o = block_latest()
|
||||
r = conn.do(o)
|
||||
block_offset = int(strip_0x(r), 16) + 1
|
||||
syncer_backend.set(block_offset, 0)
|
||||
|
||||
# Set up provisioner for common task input data
|
||||
TrafficProvisioner.oracles['token']= common.registry.TokenOracle(w3, config.get('CIC_CHAIN_SPEC'), registry)
|
||||
TrafficProvisioner.oracles['account'] = common.registry.AccountsOracle(w3, config.get('CIC_CHAIN_SPEC'), registry)
|
||||
TrafficProvisioner.default_aux = {
|
||||
'chain_spec': config.get('CIC_CHAIN_SPEC'),
|
||||
'registry': registry,
|
||||
'redis_host_callback': config.get('_REDIS_HOST_CALLBACK'),
|
||||
'redis_port_callback': config.get('_REDIS_PORT_CALLBACK'),
|
||||
'redis_db': config.get('REDIS_DB'),
|
||||
'api_queue': config.get('_CELERY_QUEUE'),
|
||||
}
|
||||
|
||||
syncer = HeadSyncer(syncer_backend, loop_callback=handler.refresh)
|
||||
syncer.add_filter(handler)
|
||||
syncer.loop(1, conn)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -2,8 +2,8 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const http = require('http');
|
||||
|
||||
const cic = require('@cicnet/cic-client-meta');
|
||||
const crdt = require('@cicnet/crdt-meta');
|
||||
const cic = require('cic-client-meta');
|
||||
const crdt = require('crdt-meta');
|
||||
|
||||
//const conf = JSON.parse(fs.readFileSync('./cic.conf'));
|
||||
|
||||
@@ -2,12 +2,12 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const http = require('http');
|
||||
|
||||
const cic = require('@cicnet/cic-client-meta');
|
||||
const crdt = require('@cicnet/crdt-meta');
|
||||
const cic = require('cic-client-meta');
|
||||
const vcfp = require('vcard-parser');
|
||||
|
||||
//const conf = JSON.parse(fs.readFileSync('./cic.conf'));
|
||||
|
||||
const config = new crdt.Config('./config');
|
||||
const config = new cic.Config('./config');
|
||||
config.process();
|
||||
console.log(config);
|
||||
|
||||
@@ -42,18 +42,24 @@ function sendit(uid, envelope) {
|
||||
}
|
||||
|
||||
function doOne(keystore, filePath, identifier) {
|
||||
const signer = new crdt.PGPSigner(keystore);
|
||||
const signer = new cic.PGPSigner(keystore);
|
||||
|
||||
const o = JSON.parse(fs.readFileSync(filePath).toString());
|
||||
//const b = Buffer.from(j['vcard'], 'base64');
|
||||
//const s = b.toString();
|
||||
//const o = vcfp.parse(s);
|
||||
//const phone = o.tel[0].value;
|
||||
|
||||
cic.Custom.toKey(identifier).then((uid) => {
|
||||
const s = new crdt.Syncable(uid, o);
|
||||
s.setSigner(signer);
|
||||
s.onwrap = (env) => {
|
||||
sendit(identifier, env);
|
||||
};
|
||||
s.sign();
|
||||
});
|
||||
//cic.Phone.toKey(phone).then((uid) => {
|
||||
//const o = fs.readFileSync(filePath, 'utf-8');
|
||||
|
||||
const s = new cic.Syncable(identifier, o);
|
||||
s.setSigner(signer);
|
||||
s.onwrap = (env) => {
|
||||
sendit(identifier, env);
|
||||
};
|
||||
s.sign();
|
||||
//});
|
||||
}
|
||||
|
||||
const privateKeyPath = path.join(config.get('PGP_EXPORTS_DIR'), config.get('PGP_PRIVATE_KEY_FILE'));
|
||||
@@ -61,7 +67,7 @@ const publicKeyPath = path.join(config.get('PGP_EXPORTS_DIR'), config.get('PGP_P
|
||||
pk = fs.readFileSync(privateKeyPath);
|
||||
pubk = fs.readFileSync(publicKeyPath);
|
||||
|
||||
new crdt.PGPKeyStore(
|
||||
new cic.PGPKeyStore(
|
||||
config.get('PGP_PASSPHRASE'),
|
||||
pk,
|
||||
pubk,
|
||||
@@ -88,7 +94,7 @@ function importMetaCustom(keystore) {
|
||||
err, files = fs.readdirSync(workDir);
|
||||
} catch {
|
||||
console.error('source directory not yet ready', workDir);
|
||||
setTimeout(importMetaCustom, batchDelay, keystore);
|
||||
setTimeout(importMetaPhone, batchDelay, keystore);
|
||||
return;
|
||||
}
|
||||
let limit = batchSize;
|
||||
@@ -122,7 +128,7 @@ function importMetaCustom(keystore) {
|
||||
if (batchCount == batchSize) {
|
||||
console.debug('reached batch size, breathing');
|
||||
batchCount=0;
|
||||
setTimeout(importMetaCustom, batchDelay, keystore);
|
||||
setTimeout(importMeta, batchDelay, keystore);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const http = require('http');
|
||||
|
||||
const cic = require('@cicnet/cic-client-meta');
|
||||
const cic = require('cic-client-meta');
|
||||
const vcfp = require('vcard-parser');
|
||||
const crdt = require('@cicnet/crdt-meta');
|
||||
const crdt = require('crdt-meta');
|
||||
|
||||
//const conf = JSON.parse(fs.readFileSync('./cic.conf'));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[pgp]
|
||||
exports_dir = ../contract-migration/testdata/pgp
|
||||
exports_dir = ../testdata/pgp
|
||||
private_key_file = privatekeys_meta.asc
|
||||
public_key_file = publickeys_meta.asc
|
||||
passphrase = merman
|
||||
@@ -1,4 +1,4 @@
|
||||
[traffic]
|
||||
#local.noop_traffic = 2
|
||||
#local.account = 2
|
||||
local.account = 2
|
||||
local.transfer = 2
|
||||
@@ -5,49 +5,10 @@
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@cicnet/cic-client-meta": "^0.0.11",
|
||||
"@cicnet/crdt-meta": "^0.0.10",
|
||||
"cic-client-meta": "0.0.7-alpha.6",
|
||||
"vcard-parser": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@cicnet/cic-client-meta": {
|
||||
"version": "0.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@cicnet/cic-client-meta/-/cic-client-meta-0.0.11.tgz",
|
||||
"integrity": "sha512-RL9CPXkWQBQzaqMoldnENC/xqinIMyWNSsejs9+qkFOWbAvC4inLdpjjCaooyvLpIZGHF9cLjxHiAdBMR9L8sQ==",
|
||||
"dependencies": {
|
||||
"@cicnet/crdt-meta": "^0.0.10",
|
||||
"@ethereumjs/tx": "^3.0.0-beta.1",
|
||||
"automerge": "^0.14.1",
|
||||
"colors": "^1.4.0",
|
||||
"ethereumjs-wallet": "^1.0.1",
|
||||
"ini": "^1.3.8",
|
||||
"openpgp": "^4.10.8",
|
||||
"pg": "^8.4.2",
|
||||
"sqlite3": "^5.0.0",
|
||||
"yargs": "^16.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"meta-get": "bin/get.js",
|
||||
"meta-set": "bin/set.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.16.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@cicnet/crdt-meta": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@cicnet/crdt-meta/-/crdt-meta-0.0.10.tgz",
|
||||
"integrity": "sha512-f+H6BQA2tE718KuNYiNzrDJN4wY00zeuhXM6aPKJUX6nryzX9g2r0yf8iDhkz+Fts1R6M7Riz73MfFEa8fgvsw==",
|
||||
"dependencies": {
|
||||
"automerge": "^0.14.2",
|
||||
"ini": "^1.3.8",
|
||||
"openpgp": "^4.10.8",
|
||||
"readline-sync": "^1.4.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.16.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@ethereumjs/common": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.2.0.tgz",
|
||||
@@ -356,6 +317,24 @@
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
||||
},
|
||||
"node_modules/cic-client-meta": {
|
||||
"version": "0.0.7-alpha.6",
|
||||
"resolved": "https://registry.npmjs.org/cic-client-meta/-/cic-client-meta-0.0.7-alpha.6.tgz",
|
||||
"integrity": "sha512-oIN1aHkPHfsxJKDV6k4f1kX2tcppw3Q+D1b4BoPh0hYjNKNb7gImBMWnGsy8uiD9W6SNYE4sIXyrtct8mvrhsw==",
|
||||
"dependencies": {
|
||||
"@ethereumjs/tx": "^3.0.0-beta.1",
|
||||
"automerge": "^0.14.1",
|
||||
"ethereumjs-wallet": "^1.0.1",
|
||||
"ini": "^1.3.5",
|
||||
"openpgp": "^4.10.8",
|
||||
"pg": "^8.4.2",
|
||||
"sqlite3": "^5.0.0",
|
||||
"yargs": "^16.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "~14.16.1"
|
||||
}
|
||||
},
|
||||
"node_modules/cipher-base": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
|
||||
@@ -439,14 +418,6 @@
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"node_modules/colors": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
|
||||
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
|
||||
"engines": {
|
||||
"node": ">=0.1.90"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
@@ -1584,14 +1555,6 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/readline-sync": {
|
||||
"version": "1.4.10",
|
||||
"resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz",
|
||||
"integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==",
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/request": {
|
||||
"version": "2.88.2",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
|
||||
@@ -2122,34 +2085,6 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@cicnet/cic-client-meta": {
|
||||
"version": "0.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@cicnet/cic-client-meta/-/cic-client-meta-0.0.11.tgz",
|
||||
"integrity": "sha512-RL9CPXkWQBQzaqMoldnENC/xqinIMyWNSsejs9+qkFOWbAvC4inLdpjjCaooyvLpIZGHF9cLjxHiAdBMR9L8sQ==",
|
||||
"requires": {
|
||||
"@cicnet/crdt-meta": "^0.0.10",
|
||||
"@ethereumjs/tx": "^3.0.0-beta.1",
|
||||
"automerge": "^0.14.1",
|
||||
"colors": "^1.4.0",
|
||||
"ethereumjs-wallet": "^1.0.1",
|
||||
"ini": "^1.3.8",
|
||||
"openpgp": "^4.10.8",
|
||||
"pg": "^8.4.2",
|
||||
"sqlite3": "^5.0.0",
|
||||
"yargs": "^16.1.0"
|
||||
}
|
||||
},
|
||||
"@cicnet/crdt-meta": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@cicnet/crdt-meta/-/crdt-meta-0.0.10.tgz",
|
||||
"integrity": "sha512-f+H6BQA2tE718KuNYiNzrDJN4wY00zeuhXM6aPKJUX6nryzX9g2r0yf8iDhkz+Fts1R6M7Riz73MfFEa8fgvsw==",
|
||||
"requires": {
|
||||
"automerge": "^0.14.2",
|
||||
"ini": "^1.3.8",
|
||||
"openpgp": "^4.10.8",
|
||||
"readline-sync": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"@ethereumjs/common": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.2.0.tgz",
|
||||
@@ -2177,9 +2112,9 @@
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "14.14.41",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.41.tgz",
|
||||
"integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g=="
|
||||
"version": "14.14.39",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.39.tgz",
|
||||
"integrity": "sha512-Qipn7rfTxGEDqZiezH+wxqWYR8vcXq5LRpZrETD19Gs4o8LbklbmqotSUsMU+s5G3PJwMRDfNEYoxrcBwIxOuw=="
|
||||
},
|
||||
"@types/pbkdf2": {
|
||||
"version": "3.1.0",
|
||||
@@ -2444,6 +2379,22 @@
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
||||
},
|
||||
"cic-client-meta": {
|
||||
"version": "0.0.7-alpha.8",
|
||||
"resolved": "https://registry.npmjs.org/cic-client-meta/-/cic-client-meta-0.0.7-alpha.8.tgz",
|
||||
"integrity": "sha512-NtU4b4dptG2gsKXIvAv1xCxxxhrr801tb8+Co1O+VLx+wvxFyPRxqa2f2eN5nrSnFnljNsWWpE6K5bJZb1+Rqw==",
|
||||
"requires": {
|
||||
"@ethereumjs/tx": "^3.0.0-beta.1",
|
||||
"automerge": "^0.14.1",
|
||||
"crdt-meta": "0.0.8",
|
||||
"ethereumjs-wallet": "^1.0.1",
|
||||
"ini": "^1.3.8",
|
||||
"openpgp": "^4.10.8",
|
||||
"pg": "^8.4.2",
|
||||
"sqlite3": "^5.0.0",
|
||||
"yargs": "^16.1.0"
|
||||
}
|
||||
},
|
||||
"cipher-base": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
|
||||
@@ -2511,11 +2462,6 @@
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"colors": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
|
||||
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
@@ -2549,6 +2495,18 @@
|
||||
"printj": "~1.1.0"
|
||||
}
|
||||
},
|
||||
"crdt-meta": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/crdt-meta/-/crdt-meta-0.0.8.tgz",
|
||||
"integrity": "sha512-CS0sS0L2QWthz7vmu6vzl3p4kcpJ+IKILBJ4tbgN4A3iNG8wnBeuDIv/z3KFFQjcfuP4QAh6E9LywKUTxtDc3g==",
|
||||
"requires": {
|
||||
"automerge": "^0.14.2",
|
||||
"ini": "^1.3.8",
|
||||
"openpgp": "^4.10.8",
|
||||
"pg": "^8.5.1",
|
||||
"sqlite3": "^5.0.2"
|
||||
}
|
||||
},
|
||||
"create-hash": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
|
||||
@@ -3457,11 +3415,6 @@
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"readline-sync": {
|
||||
"version": "1.4.10",
|
||||
"resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz",
|
||||
"integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw=="
|
||||
},
|
||||
"request": {
|
||||
"version": "2.88.2",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
|
||||
7
apps/contract-migration/scripts/package.json
Normal file
7
apps/contract-migration/scripts/package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"cic-client-meta": "^0.0.7-alpha.8",
|
||||
"crdt-meta": "0.0.8",
|
||||
"vcard-parser": "^1.0.0"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
cic-base[full_graph]==0.1.2b11
|
||||
cic-base[full_graph]==0.1.2b9
|
||||
sarafu-faucet==0.0.3a3
|
||||
cic-eth==0.11.0b14
|
||||
cic-eth==0.11.0b13
|
||||
cic-types==0.1.0a11
|
||||
crypto-dev-signer==0.4.14b3
|
||||
@@ -72,7 +72,6 @@ argparser.add_argument('--ussd-provider', type=str, dest='ussd_provider', defaul
|
||||
argparser.add_argument('--skip-custodial', dest='skip_custodial', action='store_true', help='skip all custodial verifications')
|
||||
argparser.add_argument('--exclude', action='append', type=str, default=[], help='skip specified verification')
|
||||
argparser.add_argument('--include', action='append', type=str, help='include specified verification')
|
||||
argparser.add_argument('--token-symbol', default='SRF', type=str, dest='token_symbol', help='Token symbol to use for trnsactions')
|
||||
argparser.add_argument('-r', '--registry-address', type=str, dest='r', help='CIC Registry address')
|
||||
argparser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, help='environment prefix for variables to overwrite configuration')
|
||||
argparser.add_argument('-x', '--exit-on-error', dest='x', action='store_true', help='Halt exection on error')
|
||||
@@ -102,8 +101,6 @@ config.censor('PASSWORD', 'SSL')
|
||||
config.add(args.meta_provider, '_META_PROVIDER', True)
|
||||
config.add(args.ussd_provider, '_USSD_PROVIDER', True)
|
||||
|
||||
token_symbol = args.token_symbol
|
||||
|
||||
logg.debug('config loaded from {}:\n{}'.format(config_dir, config))
|
||||
|
||||
celery_app = celery.Celery(backend=config.get('CELERY_RESULT_URL'), broker=config.get('CELERY_BROKER_URL'))
|
||||
@@ -276,10 +273,7 @@ class Verifier:
|
||||
def verify_balance(self, address, balance):
|
||||
o = self.erc20_tx_factory.balance(self.token_address, address)
|
||||
r = self.conn.do(o)
|
||||
try:
|
||||
actual_balance = int(strip_0x(r), 16)
|
||||
except ValueError:
|
||||
actual_balance = int(r)
|
||||
actual_balance = int(strip_0x(r), 16)
|
||||
balance = int(balance / 1000000) * 1000000
|
||||
logg.debug('balance for {}: {}'.format(address, balance))
|
||||
if balance != actual_balance:
|
||||
@@ -467,7 +461,7 @@ def main():
|
||||
tx = txf.template(ZERO_ADDRESS, token_index_address)
|
||||
data = add_0x(registry_addressof_method)
|
||||
h = hashlib.new('sha256')
|
||||
h.update(token_symbol.encode('utf-8'))
|
||||
h.update(b'SRF')
|
||||
z = h.digest()
|
||||
data += eth_abi.encode_single('bytes32', z).hex()
|
||||
txf.set_code(tx, data)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user