master
Bob Mottram 2019-07-13 10:37:17 +01:00
parent 72250edb29
commit a44e9f5a3c
3 changed files with 135 additions and 45 deletions

View File

@ -555,7 +555,8 @@ class PubServer(BaseHTTPRequestHandler):
self.end_headers() self.end_headers()
self.server.POSTbusy=False self.server.POSTbusy=False
def runDaemon(baseDir: str,domain: str,port=80,httpPrefix='https', \ def runDaemon(clientToServer: bool,baseDir: str,domain: str, \
port=80,httpPrefix='https', \
fedList=[],noreply=False,nolike=False,nopics=False, \ fedList=[],noreply=False,nolike=False,nopics=False, \
noannounce=False,cw=False,ocapAlways=False, \ noannounce=False,cw=False,ocapAlways=False, \
useTor=False,debug=False) -> None: useTor=False,debug=False) -> None:
@ -613,5 +614,8 @@ def runDaemon(baseDir: str,domain: str,port=80,httpPrefix='https', \
httpd.ocapAlways, \ httpd.ocapAlways, \
debug,httpd.acceptedCaps),daemon=True) debug,httpd.acceptedCaps),daemon=True)
httpd.thrInboxQueue.start() httpd.thrInboxQueue.start()
print('Running ActivityPub server on ' + domain + ' port ' + str(port)) if clientToServer:
print('Running ActivityPub client on ' + domain + ' port ' + str(port))
else:
print('Running ActivityPub server on ' + domain + ' port ' + str(port))
httpd.serve_forever() httpd.serve_forever()

View File

@ -552,6 +552,7 @@ if args.testdata:
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"another mystery solved hey",False,True,False,None,None,useBlurhash) createPublicPost(baseDir,nickname,domain,port,httpPrefix,"another mystery solved hey",False,True,False,None,None,useBlurhash)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"let's go bowling",False,True,False,None,None,useBlurhash) createPublicPost(baseDir,nickname,domain,port,httpPrefix,"let's go bowling",False,True,False,None,None,useBlurhash)
runDaemon(baseDir,domain,port,httpPrefix,federationList, \ runDaemon(args.client,baseDir,domain,port,httpPrefix, \
federationList, \
args.noreply,args.nolike,args.nopics, \ args.noreply,args.nolike,args.nopics, \
args.noannounce,args.cw,ocapAlways,useTor,debug) args.noannounce,args.cw,ocapAlways,useTor,debug)

167
tests.py
View File

@ -59,7 +59,8 @@ def testHttpsigBase(withDigest):
port=5576 port=5576
baseDir=os.getcwd() baseDir=os.getcwd()
password='SuperSecretPassword' password='SuperSecretPassword'
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,nickname,domain,port,httpPrefix,False,password) privateKeyPem,publicKeyPem,person,wfEndpoint= \
createPerson(baseDir,nickname,domain,port,httpPrefix,False,password)
messageBodyJsonStr = '{"a key": "a value", "another key": "A string"}' messageBodyJsonStr = '{"a key": "a value", "another key": "A string"}'
headersDomain=domain headersDomain=domain
@ -69,15 +70,20 @@ def testHttpsigBase(withDigest):
if not withDigest: if not withDigest:
headers = {'host': headersDomain} headers = {'host': headersDomain}
else: else:
bodyDigest = base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest()) bodyDigest = \
base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest())
headers = {'host': headersDomain, 'digest': f'SHA-256={bodyDigest}'} headers = {'host': headersDomain, 'digest': f'SHA-256={bodyDigest}'}
path='/inbox' path='/inbox'
signatureHeader = signPostHeaders(privateKeyPem, nickname, domain, port, path, httpPrefix, None) signatureHeader = \
signPostHeaders(privateKeyPem, nickname, domain, port, path, httpPrefix, None)
headers['signature'] = signatureHeader headers['signature'] = signatureHeader
assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, '/inbox' ,False, messageBodyJsonStr) assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, \
assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, '/parambulator/inbox', False , messageBodyJsonStr) == False '/inbox' ,False, messageBodyJsonStr)
assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, '/inbox', True, messageBodyJsonStr) == False assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, \
'/parambulator/inbox', False , messageBodyJsonStr) == False
assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, \
'/inbox', True, messageBodyJsonStr) == False
if not withDigest: if not withDigest:
# fake domain # fake domain
headers = {'host': 'bogon.domain'} headers = {'host': 'bogon.domain'}
@ -87,7 +93,8 @@ def testHttpsigBase(withDigest):
bodyDigest = base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest()) bodyDigest = base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest())
headers = {'host': domain, 'digest': f'SHA-256={bodyDigest}'} headers = {'host': domain, 'digest': f'SHA-256={bodyDigest}'}
headers['signature'] = signatureHeader headers['signature'] = signatureHeader
assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, '/inbox', True, messageBodyJsonStr) == False assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, \
'/inbox', True, messageBodyJsonStr) == False
def testHttpsig(): def testHttpsig():
testHttpsigBase(False) testHttpsigBase(False)
@ -117,7 +124,8 @@ def testThreads():
thr.join() thr.join()
assert thr.isAlive()==False assert thr.isAlive()==False
def createServerAlice(path: str,domain: str,port: int,federationList: [],hasFollows: bool,hasPosts :bool,ocapAlways: bool): def createServerAlice(path: str,domain: str,port: int,federationList: [], \
hasFollows: bool,hasPosts :bool,ocapAlways: bool):
print('Creating test server: Alice on port '+str(port)) print('Creating test server: Alice on port '+str(port))
if os.path.isdir(path): if os.path.isdir(path):
shutil.rmtree(path) shutil.rmtree(path)
@ -134,22 +142,33 @@ def createServerAlice(path: str,domain: str,port: int,federationList: [],hasFoll
noannounce=False noannounce=False
cw=False cw=False
useBlurhash=True useBlurhash=True
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,httpPrefix,True,password) privateKeyPem,publicKeyPem,person,wfEndpoint= \
createPerson(path,nickname,domain,port,httpPrefix,True,password)
deleteAllPosts(path,nickname,domain,'inbox') deleteAllPosts(path,nickname,domain,'inbox')
deleteAllPosts(path,nickname,domain,'outbox') deleteAllPosts(path,nickname,domain,'outbox')
if hasFollows: if hasFollows:
followPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList,True) followPerson(path,nickname,domain,'bob','127.0.0.100:61936', \
followerOfPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList,True) federationList,True)
followerOfPerson(path,nickname,domain,'bob','127.0.0.100:61936', \
federationList,True)
if hasPosts: if hasPosts:
createPublicPost(path,nickname, domain, port,httpPrefix, "No wise fish would go anywhere without a porpoise", False, True, clientToServer,None,None,useBlurhash) createPublicPost(path,nickname, domain, port,httpPrefix, \
createPublicPost(path,nickname, domain, port,httpPrefix, "Curiouser and curiouser!", False, True, clientToServer,None,None,useBlurhash) "No wise fish would go anywhere without a porpoise", \
createPublicPost(path,nickname, domain, port,httpPrefix, "In the gardens of memory, in the palace of dreams, that is where you and I shall meet", False, True, clientToServer,None,None,useBlurhash) False, True, clientToServer,None,None,useBlurhash)
createPublicPost(path,nickname, domain, port,httpPrefix, \
"Curiouser and curiouser!", False, True, \
clientToServer,None,None,useBlurhash)
createPublicPost(path,nickname, domain, port,httpPrefix, \
"In the gardens of memory, in the palace of dreams, that is where you and I shall meet", \
False, True, clientToServer,None,None,useBlurhash)
global testServerAliceRunning global testServerAliceRunning
testServerAliceRunning = True testServerAliceRunning = True
print('Server running: Alice') print('Server running: Alice')
runDaemon(path,domain,port,httpPrefix,federationList,noreply,nolike,nopics,noannounce,cw,ocapAlways,useTor,True) runDaemon(False,path,domain,port,httpPrefix,federationList, \
noreply,nolike,nopics,noannounce,cw,ocapAlways,useTor,True)
def createServerBob(path: str,domain: str,port: int,federationList: [],hasFollows: bool,hasPosts :bool,ocapAlways :bool): def createServerBob(path: str,domain: str,port: int,federationList: [], \
hasFollows: bool,hasPosts :bool,ocapAlways :bool):
print('Creating test server: Bob on port '+str(port)) print('Creating test server: Bob on port '+str(port))
if os.path.isdir(path): if os.path.isdir(path):
shutil.rmtree(path) shutil.rmtree(path)
@ -166,22 +185,33 @@ def createServerBob(path: str,domain: str,port: int,federationList: [],hasFollow
noannounce=False noannounce=False
cw=False cw=False
useBlurhash=False useBlurhash=False
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,httpPrefix,True,password) privateKeyPem,publicKeyPem,person,wfEndpoint= \
createPerson(path,nickname,domain,port,httpPrefix,True,password)
deleteAllPosts(path,nickname,domain,'inbox') deleteAllPosts(path,nickname,domain,'inbox')
deleteAllPosts(path,nickname,domain,'outbox') deleteAllPosts(path,nickname,domain,'outbox')
if hasFollows: if hasFollows:
followPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList,True) followPerson(path,nickname,domain, \
followerOfPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList,True) 'alice','127.0.0.50:61935',federationList,True)
followerOfPerson(path,nickname,domain, \
'alice','127.0.0.50:61935',federationList,True)
if hasPosts: if hasPosts:
createPublicPost(path,nickname, domain, port,httpPrefix, "It's your life, live it your way.", False, True, clientToServer,None,None,useBlurhash) createPublicPost(path,nickname, domain, port,httpPrefix, \
createPublicPost(path,nickname, domain, port,httpPrefix, "One of the things I've realised is that I am very simple", False, True, clientToServer,None,None,useBlurhash) "It's your life, live it your way.", \
createPublicPost(path,nickname, domain, port,httpPrefix, "Quantum physics is a bit of a passion of mine", False, True, clientToServer,None,None,useBlurhash) False, True, clientToServer,None,None,useBlurhash)
createPublicPost(path,nickname, domain, port,httpPrefix, \
"One of the things I've realised is that I am very simple", \
False, True, clientToServer,None,None,useBlurhash)
createPublicPost(path,nickname, domain, port,httpPrefix, \
"Quantum physics is a bit of a passion of mine", \
False, True, clientToServer,None,None,useBlurhash)
global testServerBobRunning global testServerBobRunning
testServerBobRunning = True testServerBobRunning = True
print('Server running: Bob') print('Server running: Bob')
runDaemon(path,domain,port,httpPrefix,federationList,noreply,nolike,nopics,noannounce,cw,ocapAlways,useTor,True) runDaemon(False,path,domain,port,httpPrefix,federationList, \
noreply,nolike,nopics,noannounce,cw,ocapAlways,useTor,True)
def createServerEve(path: str,domain: str,port: int,federationList: [],hasFollows: bool,hasPosts :bool,ocapAlways :bool): def createServerEve(path: str,domain: str,port: int,federationList: [], \
hasFollows: bool,hasPosts :bool,ocapAlways :bool):
print('Creating test server: Eve on port '+str(port)) print('Creating test server: Eve on port '+str(port))
if os.path.isdir(path): if os.path.isdir(path):
shutil.rmtree(path) shutil.rmtree(path)
@ -197,13 +227,15 @@ def createServerEve(path: str,domain: str,port: int,federationList: [],hasFollow
nopics=False nopics=False
noannounce=False noannounce=False
cw=False cw=False
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,httpPrefix,True,password) privateKeyPem,publicKeyPem,person,wfEndpoint= \
createPerson(path,nickname,domain,port,httpPrefix,True,password)
deleteAllPosts(path,nickname,domain,'inbox') deleteAllPosts(path,nickname,domain,'inbox')
deleteAllPosts(path,nickname,domain,'outbox') deleteAllPosts(path,nickname,domain,'outbox')
global testServerEveRunning global testServerEveRunning
testServerEveRunning = True testServerEveRunning = True
print('Server running: Eve') print('Server running: Eve')
runDaemon(path,domain,port,httpPrefix,federationList,noreply,nolike,nopics,noannounce,cw,ocapAlways,useTor,True) runDaemon(False,path,domain,port,httpPrefix,federationList, \
noreply,nolike,nopics,noannounce,cw,ocapAlways,useTor,True)
def testPostMessageBetweenServers(): def testPostMessageBetweenServers():
print('Testing sending message from one server to the inbox of another') print('Testing sending message from one server to the inbox of another')
@ -232,8 +264,16 @@ def testPostMessageBetweenServers():
bobPort=61936 bobPort=61936
federationList=[bobDomain,aliceDomain] federationList=[bobDomain,aliceDomain]
thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList,False,False,ocapAlways),daemon=True) thrAlice = \
thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList,False,False,ocapAlways),daemon=True) threadWithTrace(target=createServerAlice, \
args=(aliceDir,aliceDomain,alicePort, \
federationList,False,False, \
ocapAlways),daemon=True)
thrBob = \
threadWithTrace(target=createServerBob, \
args=(bobDir,bobDomain,bobPort, \
federationList,False,False, \
ocapAlways),daemon=True)
thrAlice.start() thrAlice.start()
thrBob.start() thrBob.start()
@ -268,7 +308,14 @@ def testPostMessageBetweenServers():
outboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/outbox' outboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/outbox'
assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==0 assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==0
sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Why is a mouse when it spins?', followersOnly, saveToFile, clientToServer,attachedImageFilename,attachedImageDescription,useBlurhash, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject) sendResult = \
sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, \
'bob', bobDomain, bobPort, ccUrl, httpPrefix, \
'Why is a mouse when it spins?', followersOnly, \
saveToFile, clientToServer,attachedImageFilename, \
attachedImageDescription,useBlurhash, federationList, \
aliceSendThreads, alicePostLog, aliceCachedWebfingers, \
alicePersonCache,inReplyTo, inReplyToAtomUri, subject)
print('sendResult: '+str(sendResult)) print('sendResult: '+str(sendResult))
queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue' queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue'
@ -293,8 +340,10 @@ def testPostMessageBetweenServers():
print('\n\n*******************************************************') print('\n\n*******************************************************')
print("Bob likes Alice's post") print("Bob likes Alice's post")
followerOfPerson(bobDir,'bob',bobDomain,'alice',aliceDomain+':'+str(alicePort),federationList,True) followerOfPerson(bobDir,'bob',bobDomain,'alice', \
followPerson(aliceDir,'alice',aliceDomain,'bob',bobDomain+':'+str(bobPort),federationList,True) aliceDomain+':'+str(alicePort),federationList,True)
followPerson(aliceDir,'alice',aliceDomain,'bob', \
bobDomain+':'+str(bobPort),federationList,True)
sessionBob = createSession(bobDomain,bobPort,useTor) sessionBob = createSession(bobDomain,bobPort,useTor)
bobSendThreads = [] bobSendThreads = []
@ -392,17 +441,29 @@ def testFollowBetweenServers():
aliceDir=baseDir+'/.tests/alice' aliceDir=baseDir+'/.tests/alice'
aliceDomain='127.0.0.42' aliceDomain='127.0.0.42'
alicePort=61935 alicePort=61935
thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList,False,False,ocapAlways),daemon=True) thrAlice = \
threadWithTrace(target=createServerAlice, \
args=(aliceDir,aliceDomain,alicePort, \
federationList,False,False, \
ocapAlways),daemon=True)
bobDir=baseDir+'/.tests/bob' bobDir=baseDir+'/.tests/bob'
bobDomain='127.0.0.64' bobDomain='127.0.0.64'
bobPort=61936 bobPort=61936
thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList,False,False,ocapAlways),daemon=True) thrBob = \
threadWithTrace(target=createServerBob, \
args=(bobDir,bobDomain,bobPort, \
federationList,False,False, \
ocapAlways),daemon=True)
eveDir=baseDir+'/.tests/eve' eveDir=baseDir+'/.tests/eve'
eveDomain='127.0.0.55' eveDomain='127.0.0.55'
evePort=61937 evePort=61937
thrEve = threadWithTrace(target=createServerEve,args=(eveDir,eveDomain,evePort,federationList,False,False,False),daemon=True) thrEve = \
threadWithTrace(target=createServerEve, \
args=(eveDir,eveDomain,evePort, \
federationList,False,False, \
False),daemon=True)
thrAlice.start() thrAlice.start()
thrBob.start() thrBob.start()
@ -481,7 +542,14 @@ def testFollowBetweenServers():
eveSendThreads=[] eveSendThreads=[]
evePostLog=[] evePostLog=[]
useBlurhash=False useBlurhash=False
sendResult = sendPost(sessionEve,eveDir,'eve', eveDomain, evePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Eve message', followersOnly, saveToFile, clientToServer,None,None,useBlurhash, federationList, eveSendThreads, evePostLog, eveCachedWebfingers,evePersonCache,inReplyTo, inReplyToAtomUri, subject) sendResult = \
sendPost(sessionEve,eveDir,'eve', eveDomain, evePort, \
'bob', bobDomain, bobPort, ccUrl, \
httpPrefix, 'Eve message', followersOnly, \
saveToFile, clientToServer,None,None, \
useBlurhash, federationList, eveSendThreads, \
evePostLog, eveCachedWebfingers, \
evePersonCache,inReplyTo, inReplyToAtomUri, subject)
print('sendResult: '+str(sendResult)) print('sendResult: '+str(sendResult))
queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue' queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue'
@ -508,7 +576,13 @@ def testFollowBetweenServers():
aliceSendThreads=[] aliceSendThreads=[]
alicePostLog=[] alicePostLog=[]
useBlurhash=False useBlurhash=False
sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Alice message', followersOnly, saveToFile, clientToServer,None,None,useBlurhash, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject) sendResult = \
sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, \
'bob', bobDomain, bobPort, ccUrl, \
httpPrefix, 'Alice message', followersOnly, saveToFile, \
clientToServer,None,None,useBlurhash, federationList, \
aliceSendThreads, alicePostLog, aliceCachedWebfingers, \
alicePersonCache,inReplyTo, inReplyToAtomUri, subject)
print('sendResult: '+str(sendResult)) print('sendResult: '+str(sendResult))
queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue' queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue'
@ -527,8 +601,12 @@ def testFollowBetweenServers():
print('\n\n*********************************************************') print('\n\n*********************************************************')
print("\nBob changes Alice's capabilities so that she can't reply on his posts") print("\nBob changes Alice's capabilities so that she can't reply on his posts")
bobCapsFilename=bobDir+'/accounts/bob@'+bobDomain+'/ocap/accept/'+httpPrefix+':##'+aliceDomain+':'+str(alicePort)+'#users#alice.json' bobCapsFilename= \
aliceCapsFilename=aliceDir+'/accounts/alice@'+aliceDomain+'/ocap/granted/'+httpPrefix+':##'+bobDomain+':'+str(bobPort)+'#users#bob.json' bobDir+'/accounts/bob@'+bobDomain+'/ocap/accept/'+ \
httpPrefix+':##'+aliceDomain+':'+str(alicePort)+'#users#alice.json'
aliceCapsFilename= \
aliceDir+'/accounts/alice@'+aliceDomain+'/ocap/granted/'+ \
httpPrefix+':##'+bobDomain+':'+str(bobPort)+'#users#bob.json'
sessionBob = createSession(bobDomain,bobPort,useTor) sessionBob = createSession(bobDomain,bobPort,useTor)
bobSendThreads = [] bobSendThreads = []
bobPostLog = [] bobPostLog = []
@ -547,7 +625,8 @@ def testFollowBetweenServers():
newCapabilities=["inbox:write","objects:read","inbox:noreply"] newCapabilities=["inbox:write","objects:read","inbox:noreply"]
sendCapabilitiesUpdate(sessionBob,bobDir,httpPrefix, \ sendCapabilitiesUpdate(sessionBob,bobDir,httpPrefix, \
'bob',bobDomain,bobPort, \ 'bob',bobDomain,bobPort, \
httpPrefix+'://'+aliceDomain+':'+str(alicePort)+'/users/alice', httpPrefix+'://'+aliceDomain+':'+\
str(alicePort)+'/users/alice',
newCapabilities, \ newCapabilities, \
bobSendThreads, bobPostLog, \ bobSendThreads, bobPostLog, \
bobCachedWebfingers,bobPersonCache, \ bobCachedWebfingers,bobPersonCache, \
@ -598,8 +677,14 @@ def testFollowBetweenServers():
thrEve.join() thrEve.join()
assert thrEve.isAlive()==False assert thrEve.isAlive()==False
assert os.path.isfile(bobDir+'/accounts/bob@'+bobDomain+'/ocap/accept/'+httpPrefix+':##'+aliceDomain+':'+str(alicePort)+'#users#alice.json') assert os.path.isfile(bobDir+'/accounts/bob@'+bobDomain+ \
assert os.path.isfile(aliceDir+'/accounts/alice@'+aliceDomain+'/ocap/granted/'+httpPrefix+':##'+bobDomain+':'+str(bobPort)+'#users#bob.json') '/ocap/accept/'+httpPrefix+':##'+ \
aliceDomain+':'+str(alicePort)+ \
'#users#alice.json')
assert os.path.isfile(aliceDir+'/accounts/alice@'+ \
aliceDomain+'/ocap/granted/'+ \
httpPrefix+':##'+bobDomain+':'+ \
str(bobPort)+'#users#bob.json')
assert 'alice@'+aliceDomain in open(bobDir+'/accounts/bob@'+bobDomain+'/followers.txt').read() assert 'alice@'+aliceDomain in open(bobDir+'/accounts/bob@'+bobDomain+'/followers.txt').read()
assert 'bob@'+bobDomain in open(aliceDir+'/accounts/alice@'+aliceDomain+'/following.txt').read() assert 'bob@'+bobDomain in open(aliceDir+'/accounts/alice@'+aliceDomain+'/following.txt').read()