def post_shutdown(self): lwc = LedgerWebClient(self.url) msg = shutdown_message.ShutdownMessage({}) msg.SenderID = self._admin_node.Address msg.sign_from_node(self._admin_node) try: lwc.post_message(msg) except MessageException as me: print me
def post_shutdown(self): lwc = LedgerWebClient(self.Url) msg = shutdown_message.ShutdownMessage({}) msg.SenderID = self.AdminNode.Address msg.sign_from_node(self.AdminNode) try: lwc.post_message(msg) except MessageException as me: print me
def __init__(self, baseurl, keystring=None): cmd.Cmd.__init__(self) self.prompt = 'client> ' self.CurrentState = {} self.LedgerWebClient = LedgerWebClient(baseurl) signingkey = generate_signing_key( wifstr=keystring) if keystring else generate_signing_key() identifier = generate_identifier(signingkey) self.LocalNode = Node(identifier=identifier, signingkey=signingkey, name="txnclient")
def is_started(self, url=None): if not url: url = self.url lwc = LedgerWebClient(url) sta = None try: sta = lwc.get_status(verbose=False, timeout=2) except MessageException as e: print e.message return False if sta is not None: return sta.get('Status', '') == 'started' return False
def _get_webclient(args): if args.url is not None: url = args.url else: url = 'http://localhost:8800' return LedgerWebClient(url)
def test_adding_node_with_nodelist(self): try: validators = self.vnm.launch_network(5) validator_urls = self.vnm.urls() endpoint_client = EndpointRegistryClient(validator_urls[0]) nodes = [] for epl in endpoint_client.get_endpoint_list(): node = {} node['Host'] = epl['Host'] node['Port'] = epl['Port'] node['Identifier'] = epl['NodeIdentifier'] node['NodeName'] = epl['Name'] nodes.append(node) peers = [nodes[0]['NodeName'], nodes[2]['NodeName'], 'validator-x'] self.vnm.validator_config['Nodes'] = nodes self.vnm.validator_config['Peers'] = peers v = self.vnm.launch_node() validator_urls.append(v.url) self.vnm.wait_for_registration([v], validators[0]) ledger_web_clients = [ LedgerWebClient(url=u) for u in validator_urls ] integer_key_clients = [ IntegerKeyClient(baseurl=u, keystring=generate_private_key()) for u in validator_urls ] for int_key_client in integer_key_clients: int_key_client.set(key=str(1), value=20) self._verify_equality_of_block_lists(ledger_web_clients) finally: self.vnm.shutdown() self.vnm.create_result_archive('TestNodeList.tar.gz')
def _get_quorum(gossiper, callback): """Attempts to connect gossiper to new available quorum nodes Args: gossiper (Node): The local node. callback (function): The function to call once the quorum topology update has completed. """ # find out how many we have and how many nodes we still need need count = max(0, TargetConnectivity - len(gossiper.VotingQuorum.keys())) # we have all the nodes we need; do next operation (the callback) if count <= 0: logger.debug('sufficiently connected via %s', [str(x.Name) for x in gossiper.VotingQuorum.itervalues()]) callback() return # add nodes we don't have already, in random order candidates = [ x for x in gossiper.quorum_list() if gossiper.VotingQuorum.get(x.Identifier, None) is None ] random.shuffle(candidates) logger.debug('trying to increase working quorum by %d from candidates %s', count, [str(x.Name) for x in candidates]) for nd in candidates: lwc = LedgerWebClient('http://{0}:{1}'.format(nd.NetHost, nd.HttpPort)) try: status = lwc.get_status(verbose=False, timeout=2) except MessageException as e: logger.debug(e.message) continue status = status.get('Status', '') if status in [ 'started', "transferring ledger", "waiting for initial connections" ]: # candidate node is live; add it logger.debug('adding %s to quorum', nd.Name) gossiper.add_quorum_node(nd) if nd.Identifier not in gossiper.peer_id_list(): send_connection_request(gossiper, nd) count -= 1 if count == 0: logger.debug('now sufficiently connected') break # try again (or possibly execute the piggybacked callback) reactor.callLater(TimeBetweenProbes, _get_quorum, gossiper, callback)
def get_statuslist(urls): ret = None try: ret = [(LedgerWebClient(url=u)).get_status() for u in urls] except Exception as e: print e raise return ret
def test_store_url(self): lwc = LedgerWebClient("http://localhost:8800") self.assertEquals( lwc.store_url(endpoint_registry.EndpointRegistryTransaction), "http://localhost:8800/store/EndpointRegistryTransaction") self.assertEquals( lwc.store_url(endpoint_registry.EndpointRegistryTransaction, 't1'), "http://localhost:8800/store/EndpointRegistryTransaction/t1") self.assertEquals( lwc.store_url(endpoint_registry.EndpointRegistryTransaction, blockid='b2'), "http://localhost:8800/store/EndpointRegistryTransaction" "?blockid=b2") self.assertEquals( lwc.store_url(endpoint_registry.EndpointRegistryTransaction, 't1', 'b2'), "http://localhost:8800/store/EndpointRegistryTransaction/t1" "?blockid=b2") self.assertEquals( lwc.store_url(endpoint_registry.EndpointRegistryTransaction, 't1', delta=True), "http://localhost:8800/store/EndpointRegistryTransaction/t1" "?delta=1") self.assertEquals( lwc.store_url(endpoint_registry.EndpointRegistryTransaction, 't1', blockid='b3', delta=True), "http://localhost:8800/store/EndpointRegistryTransaction/t1" "?blockid=b3&delta=1")
def test_join_after_delay_start(self): delayed_validator = None validator_urls = [] try: self.vnm.launch_network(5) validator_urls = self.vnm.urls() delayed_validator = self.vnm.launch_node(delay=True) time.sleep(5) command_url = delayed_validator.url + '/command' request = urllib2.Request( url=command_url, headers={'Content-Type': 'application/json'}) response = urllib2.urlopen(request, data='{"action": "start"}') response.close() self.assertEqual(response.code, 200, "Successful post to delayed validator") validator_urls.append(delayed_validator.url) ledger_web_clients = [ LedgerWebClient(url=u) for u in validator_urls ] with Progress("Waiting for registration of 1 validator") as p: url = validator_urls[0] to = TimeOut(60) while not delayed_validator.is_registered(url): if to(): raise ExitError( "{} delayed validator failed to register " "within {}S.".format(1, to.WaitTime)) p.step() time.sleep(1) try: delayed_validator.check_error() except ValidatorManagerException as vme: delayed_validator.dump_log() delayed_validator.dump_stderr() raise ExitError(str(vme)) integer_key_clients = [ IntegerKeyClient(baseurl=u, keystring=generate_private_key()) for u in validator_urls ] for int_key_client in integer_key_clients: int_key_client.set(key=str(1), value=20) self._verify_equality_of_block_lists(ledger_web_clients) finally: self.vnm.shutdown() if delayed_validator is not None and \ validator_urls is not [] and \ delayed_validator.url not in validator_urls: delayed_validator.shutdown() self.vnm.create_result_archive("TestDelayedStart.tar.gz")
def get_blocklists(urls): ret = None try: ret = [(LedgerWebClient(url=u)).get_block_list() for u in urls] except Exception as e: print e.message raise for arr in ret: arr.reverse() return ret
def _get_quorum(gossiper, callback): """Attempts to connect gossiper to new available quorum nodes Args: gossiper (Node): The local node. callback (function): The function to call once the quorum topology update has completed. """ # find out how many we have and how many nodes we still need need count = max(0, TargetConnectivity - len(gossiper.VotingQuorum.keys())) # we have all the nodes we need; do next operation (the callback) if count <= 0: logger.debug('sufficiently connected via %s', [str(x.Name) for x in gossiper.VotingQuorum.itervalues()]) callback() return # add nodes we don't have already, in random order candidates = [x for x in gossiper.quorum_list() if gossiper.VotingQuorum.get(x.Identifier, None) is None] random.shuffle(candidates) logger.debug('trying to increase working quorum by %d from candidates %s', count, [str(x.Name) for x in candidates]) for nd in candidates: lwc = LedgerWebClient('http://{0}:{1}'.format(nd.NetHost, nd.HttpPort)) try: status = lwc.get_status(verbose=False, timeout=2) except MessageException as e: logger.debug(e.message) continue status = status.get('Status', '') if status in ['started', "transferring ledger", "waiting for initial connections"]: # candidate node is live; add it logger.debug('adding %s to quorum', nd.Name) gossiper.add_quorum_node(nd) if nd.Identifier not in gossiper.peer_id_list(): send_connection_request(gossiper, nd) count -= 1 if count == 0: logger.debug('now sufficiently connected') break # try again (or possibly execute the piggybacked callback) reactor.callLater(TimeBetweenProbes, _get_quorum, gossiper, callback)
def do_block(args): subcommands = ['list', 'show'] if args.subcommand not in subcommands: print 'Unknown sub-command, expecting one of {0}'.format(subcommands) return if args.url is not None: url = args.url else: url = 'http://localhost:8800' web_client = LedgerWebClient(url) try: if args.subcommand == 'list': if args.all: blockids = web_client.get_block_list() else: blockids = web_client.get_block_list(args.blockcount) for block_id in blockids: print block_id return elif args.subcommand == 'show': if args.key is not None: block_info = web_client.get_block(args.blockID, args.key) else: block_info = web_client.get_block(args.blockID) print pretty_print_dict(block_info) return except MessageException as e: raise CliException(e)
def do_store(args): subcommands = ['list', 'show'] if args.subcommand not in subcommands: print 'Unknown sub-command, expecting one of {0}'.format(subcommands) return if args.url is not None: url = args.url else: url = 'http://localhost:8800' web_client = LedgerWebClient(url) try: if args.subcommand == 'list': transaction_type_name = web_client.get_store_by_name() print pretty_print_dict(transaction_type_name) return elif args.subcommand == 'show': if args.blockID is not None: block_id = args.blockID else: block_id = '' if args.key is not None: key = args.key else: key = '' store_info = web_client.get_store_by_name(args.transactionTypeName, key, block_id, args.incremental) print pretty_print_dict(store_info) return except MessageException as e: raise CliException(e)
def do_transaction(args): subcommands = ['list', 'show', 'status'] if args.subcommand not in subcommands: print 'Unknown sub-command, expecting one of {0}'.format(subcommands) return if args.url is not None: url = args.url else: url = 'http://localhost:8800' web_client = LedgerWebClient(url) try: if args.subcommand == 'list': if args.all: tsctids = web_client.get_transaction_list() else: tsctids = web_client.get_transaction_list(args.blockcount) for txn_id in tsctids: print txn_id return elif args.subcommand == 'show': if args.key is not None: tsct_info = web_client.get_transaction(args.transactionID, args.key) else: tsct_info = web_client.get_transaction(args.transactionID) print pretty_print_dict(tsct_info) return elif args.subcommand == 'status': tsct_status = web_client.get_transaction_status(args.transactionID) if tsct_status == transaction.Status.committed: print 'transaction committed' elif tsct_status == transaction.Status.pending: print 'transaction still uncommitted' elif tsct_status == transaction.Status.unknown: print 'unknown transaction' elif tsct_status == transaction.Status.failed: print 'transaction failed to validate.' else: print 'transaction returned unexpected status code {0}'\ .format(tsct_status) return except MessageException as e: raise CliException(e)
def test_basic_startup(self): try: self.vnm.launch_network(count=self.number_of_daemons, others_daemon=True) validator_urls = self.vnm.urls() # IntegerKeyClient is only needed to send one more transaction # so n-1=number of EndpointRegistryTransactions integer_key_clients = [ IntegerKeyClient(baseurl=u, keystring=generate_private_key()) for u in validator_urls ] ledger_web_clients = [ LedgerWebClient(url=u) for u in validator_urls ] for int_key_client in integer_key_clients: int_key_client.set(key=str(1), value=20) self._verify_equality_of_block_lists(ledger_web_clients) finally: self.vnm.shutdown() self.vnm.create_result_archive('TestDaemonStartup.tar.gz')
def test_initial_connectivity_n_minus_1(self): try: self.vnm.validator_config['LedgerURL'] = "**none**" self.vnm.validator_config['Restore'] = False validator = self.vnm.launch_node(genesis=True) validators = [validator] with Progress("Launching validator network") as p: self.vnm.validator_config['LedgerURL'] = validator.url self.vnm.validator_config['Restore'] = False node_identifiers = [validator.Address] for i in range(1, 5): self.vnm.validator_config['InitialConnectivity'] = i v = self.vnm.launch_node(genesis=False, daemon=False) validators.append(v) node_identifiers.append(v.Address) p.step() self.vnm.wait_for_registration(validators, validator) validator_urls = self.vnm.urls() ledger_web_clients = [ LedgerWebClient(url=u) for u in validator_urls ] integer_key_clients = [ IntegerKeyClient(baseurl=u, keystring=generate_private_key()) for u in validator_urls ] for int_key_client in integer_key_clients: int_key_client.set(key=str(1), value=20) self._verify_equality_of_block_lists(ledger_web_clients) self._verify_orderly_transactions(ledger_web_clients, node_identifiers) finally: self.vnm.shutdown() self.vnm.create_result_archive( 'TestOrderlyInitialConnectivity.tar.gz')
def _run_int_load( self, config, num_nodes, archive_name, tolerance=2, standard=5, block_id=True, static_network=None, vnm_timeout=None, txn_timeout=None, n_keys=100, n_runs=2, ): """ This test is getting really beat up and needs a refactor Args: config (dict): Default config for each node num_nodes (int): Total number of nodes in network simulation archive_name (str): Name for tarball summary of test results tolerance (int): Length in blocks of permissible fork (if forks are permissible) standard (int): A variable intended to guarantee that our block level identity checks have significant data to operate on. Conceptually, depends on the value of tolerance: case(tolerance): 0: minimum # of blocks required per validator otherwise: minimum # of converged blocks required per divergent block (per validator) Motivation: We want to compare identity across the network on some meaningfully large set of blocks. Introducing fork tolerance is problematic: the variable tolerance which is used to trim the ends of each ledger's block-chain could be abused to trivialize the test. Therefore, as tolerance is increased (if non-zero), we use standard to proportionally increase the minimum number of overall blocks required by the test. block_id (bool): check for block (hash) identity static_network (StaticNetworkConfig): optional static network configuration vnm_timeout (int): timeout for initiating network txn_timeout (int): timeout for batch transactions """ vnm = None try: test = IntKeyLoadTest(timeout=txn_timeout) if "TEST_VALIDATOR_URLS" not in os.environ: print "Launching validator network." vnm_config = config vnm = ValidatorNetworkManager(http_port=9000, udp_port=9100, cfg=vnm_config, static_network=static_network) vnm.launch_network(num_nodes, max_time=vnm_timeout) urls = vnm.urls() else: print "Fetching Urls of Running Validators" # TEST_VALIDATORS_RUNNING is a list of validators urls # separated by commas. # e.g. 'http://localhost:8800,http://localhost:8801' urls = str(os.environ["TEST_VALIDATOR_URLS"]).split(",") print "Testing transaction load." test.setup(urls, n_keys) test.run(n_runs) test.validate() if block_id: # check for block id convergence across network: sample_size = max(1, tolerance) * standard print "Testing block-level convergence with min sample size:", print " %s (after tolerance: %s)" % (sample_size, tolerance) # ...get all blockids from each server, newest last block_lists = [ LedgerWebClient(x).get_block_list() for x in urls ] for ls in block_lists: ls.reverse() # ...establish preconditions max_mag = len(max(block_lists, key=len)) min_mag = len(min(block_lists, key=len)) self.assertGreaterEqual( tolerance, max_mag - min_mag, 'block list magnitude differences (%s) ' 'exceed tolerance (%s)' % (max_mag - min_mag, tolerance)) effective_sample_size = max_mag - tolerance print 'effective sample size: %s' % effective_sample_size self.assertGreaterEqual( effective_sample_size, sample_size, 'not enough target samples to determine convergence') # ...(optionally) permit reasonable forks by normalizing lists if tolerance > 0: block_lists = [ block_list[0:effective_sample_size] for block_list in block_lists ] # ...id-check (possibly normalized) cross-server block chains for (i, block_list) in enumerate(block_lists): self.assertEqual( block_lists[0], block_list, '%s is divergent:\n\t%s vs.\n\t%s' % (urls[i], block_lists[0], block_list)) if vnm: vnm.shutdown() except Exception: print "Exception encountered in test case." traceback.print_exc() if vnm: vnm.shutdown() raise finally: if vnm: vnm.create_result_archive("%s.tar.gz" % archive_name) else: print "No Validator data and logs to preserve"
def _get_blocklists(self, urls): ret = [(LedgerWebClient(url=u)).get_block_list() for u in urls] for (i, arr) in enumerate(ret): arr.reverse() ret[i] = [hsh[:4] for hsh in arr] return ret
class ClientController(cmd.Cmd): _TxnVerbMap = {'=': 'set', '+=': 'inc', '-=': 'dec'} pformat = 'client> ' def __init__(self, baseurl, keystring=None): cmd.Cmd.__init__(self) self.prompt = 'client> ' self.CurrentState = {} self.LedgerWebClient = LedgerWebClient(baseurl) signingkey = generate_signing_key( wifstr=keystring) if keystring else generate_signing_key() identifier = generate_identifier(signingkey) self.LocalNode = Node(identifier=identifier, signingkey=signingkey, name="txnclient") def postcmd(self, flag, line): return flag def sign_and_post(self, msg): msg.SenderID = self.LocalNode.Identifier msg.sign_from_node(self.LocalNode) try: result = self.LedgerWebClient.post_message(msg) if result: pretty_print_dict(result) except MessageException as me: print me # ================================================================= # COMMANDS # ================================================================= def do_set(self, args): """ set -- Command to set properties of the interpreter set url --url <url> set nodeid --name <name> --keyfile <file> """ pargs = args.split() if len(pargs) == 0: print 'missing subcommand url|nodeid' return try: if pargs[0] == 'url': parser = argparse.ArgumentParser() parser.add_argument('--url', help='url used to connect to a validator', required=True) options = parser.parse_args(pargs) self.BaseURL = options.url print "server URL set to {0}".format(self.BaseURL) return elif pargs[0] == 'nodeid': pargs = args.split() parser = argparse.ArgumentParser() parser.add_argument('--name', help='name to use for the client', default='txnclient') parser.add_argument('--keyfile', help='name of the file that contains ' 'the wif format private key') options = parser.parse_args(pargs[1:]) addr = (socket.gethostbyname("localhost"), 0) name = options.name if options.keyfile: signingkey = generate_signing_key( wifstr=read_key_file(options.keyfile)) else: signingkey = generate_signing_key() identifier = generate_identifier(signingkey) self.LocalNode = Node(address=addr, identifier=identifier, signingkey=signingkey, name=name) print "local id set to {0}".format(self.LocalNode) return else: print "unknown subcommand; {0}".format(pargs[0]) return except Exception as e: print 'an error occured processing {0}: {1}'.format(args, str(e)) return def do_state(self, args): """ state -- Command to manipulate the current ledger state state fetch --store <store> state keys state value --path <path> """ pargs = args.split() if len(pargs) == 0: print 'missing subcommand: fetch|keys|value' return try: if pargs[0] == 'fetch': parser = argparse.ArgumentParser() parser.add_argument('--store', choices=TransactionTypes.keys(), default='endpoint') options = parser.parse_args(pargs[1:]) self.CurrentState = self.LedgerWebClient.get_store( TransactionTypes.get(options.store), key='*') elif pargs[0] == 'keys': try: print self.CurrentState.keys() except: print '[]' elif pargs[0] == 'value': parser = argparse.ArgumentParser() parser.add_argument('--path', required=True) options = parser.parse_args(pargs[1:]) pathargs = options.path.split('.') value = self.CurrentState while pathargs: value = value.get(pathargs.pop(0)) print value except Exception as e: print 'an error occured processing {0}: {1}'.format(args, str(e)) return def do_txn(self, args): """ txn -- Command to create IntegerKey transactions txn <expr> [ && <expr> ]* """ txn = integer_key.IntegerKeyTransaction() # pylint: disable=line-too-long pattern = re.compile(r"^\s*(?P<name>[a-zA-Z0-9]+)\s*(?P<verb>[+-]?=)\s*(?P<value>[0-9]+)\s*$") # noqa expressions = args.split('&&') for expression in expressions: match = pattern.match(expression) if not match: print 'unable to parse the transaction; {0}'.format(expression) return update = integer_key.Update() update.Verb = self._TxnVerbMap[match.group('verb')] update.Name = match.group('name') update.Value = long(match.group('value')) txn.Updates.append(update) txn.sign_from_node(self.LocalNode) msg = integer_key.IntegerKeyTransactionMessage() msg.Transaction = txn self.sign_and_post(msg) def do_nodestats(self, args): """ nodestats -- Command to send nodestats messages to validator pool nodestats reset --metrics [<metric>]+ nodestats dump --metrics [<metric>]+ --domains [<domain>]+ """ pargs = args.split() if len(pargs) == 0: print 'missing subcommand: reset|dump' return try: if pargs[0] == 'reset': parser = argparse.ArgumentParser() parser.add_argument('--metrics', default=[], nargs='+') options = parser.parse_args(pargs[1:]) self.sign_and_post(gossip_debug.ResetStatsMessage( {'MetricList': options.metrics})) return elif pargs[0] == 'dump': parser = argparse.ArgumentParser() parser.add_argument('--metrics', default=[], nargs='+') parser.add_argument('--domains', default=[], nargs='+') options = parser.parse_args(pargs[1:]) self.sign_and_post(gossip_debug.DumpNodeStatsMessage( {'MetricList': options.metrics, 'DomainList': options.domains})) return except Exception as e: print 'an error occured processing {0}: {1}'.format(args, str(e)) return print 'unknown nodestats command {0}'.format(args) def do_peerstats(self, args): """ peerstats -- Command to send peerstats messages to validator pool peerstats reset --metrics [<metric>]+ peerstats dump --metrics [<metric>]+ --peers [<nodeid>]+ """ pargs = args.split() if len(pargs) == 0: print 'missing subcommand: reset|dump' return try: if pargs[0] == 'reset': parser = argparse.ArgumentParser() parser.add_argument('--metrics', default=[], nargs='+') options = parser.parse_args(pargs[1:]) self.sign_and_post(gossip_debug.ResetPeerStatsMessage( {'MetricList': options.metrics})) return elif pargs[0] == 'dump': parser = argparse.ArgumentParser() parser.add_argument('--metrics', default=[], nargs='+') parser.add_argument('--peers', default=[], nargs='+') options = parser.parse_args(pargs[1:]) self.sign_and_post(gossip_debug.DumpPeerStatsMessage( {'MetricList': options.metrics, 'PeerIDList': options.peers})) return except Exception as e: print 'an error occured processing {0}: {1}'.format(args, str(e)) return print 'unknown peerstats command {0}'.format(args) def do_ping(self, args): """ ping -- Command to send a ping message to validator pool """ self.sign_and_post(gossip_debug.PingMessage({})) def do_sping(self, args): """ sping -- Command to send a special ping message to validator pool """ parser = argparse.ArgumentParser() parser.add_argument( '--address', default=self.LocalNode.Identifier) parser.add_argument('--count', default=2, type=int) options = parser.parse_args(args.split()) msg = SpecialPingMessage() msg.Address = options.address msg.Count = options.count self.sign_and_post(msg) def do_dumpquorum(self, args): """ dumpquorum -- Command to request quorum consensus node to dump quorum list """ self.sign_and_post(quorum_debug.DumpQuorumMessage({})) def do_dumpcnxs(self, args): """ dumpcnxs -- Command to send to the validator pool a request to dump current connection information """ self.sign_and_post(gossip_debug.DumpConnectionsMessage({})) def do_dumpblks(self, args): """ dumpblks -- Command to send dump blocks request to the validator pool dumpblks --blocks <count> """ parser = argparse.ArgumentParser() parser.add_argument('--blocks', default=0, type=int) options = parser.parse_args(args.split()) self.sign_and_post(gossip_debug.DumpJournalBlocksMessage( {'Count': options.blocks})) def do_val(self, args): """ val -- Command to send a dump value request to the validator pool val <name> [--type <transaction type>] """ pargs = args.split() parser = argparse.ArgumentParser() parser.add_argument('--type', help='Transaction family', default='/IntegerKeyTransaction') options = parser.parse_args(pargs[1:]) tinfo = {'Name': pargs[0], 'TransactionType': options.type} self.sign_and_post(journal_debug.DumpJournalValueMessage(tinfo)) def do_shutdown(self, args): """ shutdown -- Command to send a shutdown message to the validator pool """ self.sign_and_post(shutdown_message.ShutdownMessage({})) return True def do_exit(self, args): """exit Shutdown the simulator and exit the command loop """ return True def do_eof(self, args): return True
def test_store_url(self): lwc = LedgerWebClient("http://localhost:8800") self.assertEquals( lwc.store_url(endpoint_registry.EndpointRegistryTransaction), "http://localhost:8800/store/EndpointRegistryTransaction") self.assertEquals( lwc.store_url(endpoint_registry.EndpointRegistryTransaction, 't1'), "http://localhost:8800/store/EndpointRegistryTransaction/t1") self.assertEquals( lwc.store_url(endpoint_registry.EndpointRegistryTransaction, blockid='b2'), "http://localhost:8800/store/EndpointRegistryTransaction" "?blockid=b2") self.assertEquals( lwc.store_url(endpoint_registry.EndpointRegistryTransaction, 't1', 'b2'), "http://localhost:8800/store/EndpointRegistryTransaction/t1" "?blockid=b2") self.assertEquals( lwc.store_url(endpoint_registry.EndpointRegistryTransaction, 't1', delta=True), "http://localhost:8800/store/EndpointRegistryTransaction/t1" "?delta=1") self.assertEquals( lwc.store_url(endpoint_registry.EndpointRegistryTransaction, 't1', blockid='b3', delta=True), "http://localhost:8800/store/EndpointRegistryTransaction/t1" "?blockid=b3&delta=1") self.assertEquals( lwc.store_url_by_name("/EndpointRegistryTransaction"), "http://localhost:8800/store/EndpointRegistryTransaction") self.assertEquals( lwc.store_url_by_name("/EndpointRegistryTransaction", 't1'), "http://localhost:8800/store/EndpointRegistryTransaction/t1") self.assertEquals( lwc.store_url_by_name("/EndpointRegistryTransaction", blockid='b2'), "http://localhost:8800/store/EndpointRegistryTransaction" "?blockid=b2") self.assertEquals( lwc.store_url_by_name("/EndpointRegistryTransaction", 't1', 'b2'), "http://localhost:8800/store/EndpointRegistryTransaction/t1" "?blockid=b2") self.assertEquals( lwc.store_url_by_name("/EndpointRegistryTransaction", 't1', delta=True), "http://localhost:8800/store/EndpointRegistryTransaction/t1" "?delta=1") self.assertEquals( lwc.store_url_by_name("/EndpointRegistryTransaction", 't1', blockid='b3', delta=True), "http://localhost:8800/store/EndpointRegistryTransaction/t1" "?blockid=b3&delta=1")