#!/usr/bin/env node
var WebSocketServer = require('websocket').server;
var http = require('http');
var server = http.createServer(function(request, response) {
console.log((new Date()) + ' Received request for ' + request.url);
response.writeHead(404);
response.end();
});
server.listen(8080, function() {
console.log((new Date()) + ' Server is listening on port 8080');
});
wsServer = new WebSocketServer({
httpServer: server,
// You should not use autoAcceptConnections for production
// applications, as it defeats all standard cross-origin protection
// facilities built into the protocol and the browser. You should
// *always* verify the connection's origin and decide whether or not
// to accept it.
autoAcceptConnections: false
});
function originIsAllowed(origin) {
// put logic here to detect whether the specified origin is allowed.
return true;
}
var jConnections = new Array();
wsServer.on('request', function(request) {
if (!originIsAllowed(request.origin)) {
// Make sure we only accept requests from an allowed origin
request.reject();
console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
return;
}
var connection = request.accept('echo-protocol', request.origin);
var jConnectionIdentity = new Date();
jConnections.push(new Array(connection,jConnectionIdentity) );
//console.log("datestamp"+jConnections[0][1]);
console.log((new Date()) + ' Connection accepted.');
connection.on('message', function(message) {
if (message.type === 'utf8') {
console.log('Received Message: ' + message.utf8Data);
//connection.sendUTF(message.utf8Data);
for(var i=0;i<jConnections.length;i++)
{
var jConnection = jConnections[i][0];
jConnection.sendUTF(message.utf8Data);
}
}
else if (message.type === 'binary') {
console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
//connection.sendBytes(message.binaryData);
for(var i=0;i<jConnections.length;i++)
{
var jConnection = jConnections[i][0];
jConnection.sendBytes(message.binaryData);
}
}
});
connection.on('close', function(reasonCode, description) {
for(var i=0;i<jConnections.length;i++)
{
if(jConnections[i][1] == jConnectionIdentity)
{
delete jConnections[i];
console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
console.log(jConnections.length);
}
}
});
});
If I open 3 client connections and send a message from each client and then close the third client I am seeing that the output of "console.log(jConnections.length);" is still three. Upon closing the third client and sending a message from client two a crash of NodeJS occurs. My console output looks like this:
C:\Program Files\nodejs>node.exe workingexample3.js
Mon May 20 2013 16:30:14 GMT-0700 (Pacific Daylight Time) Server is listening on port 8080
Mon May 20 2013 16:30:18 GMT-0700 (Pacific Daylight Time) Connection accepted.
Mon May 20 2013 16:30:20 GMT-0700 (Pacific Daylight Time) Connection accepted.
Mon May 20 2013 16:30:22 GMT-0700 (Pacific Daylight Time) Connection accepted.
Received Message: client1
Received Message: client2
Received Message: client3
Mon May 20 2013 16:30:43 GMT-0700 (Pacific Daylight Time) Peer 127.0.0.1 disconnected.
3
Received Message: client2
C:\Program Files\nodejs\workingexample3.js:48
var jConnection = jConnections[i][0];
^
TypeError: Cannot read property '0' of undefined
at WebSocketConnection.<anonymous> (C:\Program Files\nodejs\workingexample3.js:48:38)
at WebSocketConnection.EventEmitter.emit (events.js:95:17)
at WebSocketConnection.processFrame (C:\Program Files\nodejs\node_modules\websocket\lib\WebSocketConnection.js:403:26)
at WebSocketConnection.handleSocketData (C:\Program Files\nodejs\node_modules\websocket\lib\WebSocketConnection.js:247:14)
at Socket.EventEmitter.emit (events.js:95:17)
at Socket.<anonymous> (_stream_readable.js:736:14)
at Socket.EventEmitter.emit (events.js:92:17)
at emitReadable_ (_stream_readable.js:408:10)
at emitReadable (_stream_readable.js:404:5)
at readableAddChunk (_stream_readable.js:165:9)
C:\Program Files\nodejs>
I was hoping that upon the delete statement the value of jConnections.length would be decreasing by one but that does not seem to be happening. Since all three of my clients are connecting from localhost 127.0.0.1 I had to come up with a plan to identify each connection by something other than the remoteAdress property. That's why I am pushing a Date in addition to the connection object. I am hoping to id each connection by the Date it was created on.
1) After you call delete your array looks like this -
[[1, 1], [2, 2], undefined]
The length is still 3 :)
Related
I seem unable to terminate a websocket connection from the server side of the connection using the recommended approach of calling terminate() on the ServerBinding.
I have included code below that establishes a connection, terminates it from the server side, waits for termination, then sends messages from the client. All messages are successfully handled by the server after termination.
How do I terminate a web-socket connection from the server side?
public class AkkaWebSocketServerTerminateTest {
private static final Logger LOGGER = LoggerFactory.getLogger(AkkaWebSocketServerTerminateTest.class);
#Test
public void clientConnectsServer_ServerGracefullyTerminatesConnection() throws ExecutionException, InterruptedException, TimeoutException {
ActorSystem system = ActorSystem.create();
Materializer materializer = ActorMaterializer.create(system);
Http http = Http.get(system);
/* SERVER */
Flow<Message, Message, CompletionStage<Done>> serverSideHandlerFlow = Flow
.of(Message.class)
.via(WebsocketLayer.messageToStringFlow())
.map(s -> {
LOGGER.debug("handling {}", s);
return "handled " + s;
})
.map(s -> (Message) TextMessage.create(s))
.alsoToMat(Sink.ignore(), Keep.right());
CompletionStage<ServerBinding> serverBindingCompletionStage = http.bindAndHandleSync(
httpRequest -> WebSocket.handleWebSocketRequestWith(httpRequest, serverSideHandlerFlow),
ConnectHttp.toHost("localhost", 9999),
materializer);
ServerBinding serverBinding = serverBindingCompletionStage.toCompletableFuture().get(3, TimeUnit.SECONDS);// wait for binding
/* CLIENT */
Sink<Message, CompletionStage<Done>> sink
= Flow.of(Message.class)
.via(WebsocketLayer.messageToStringFlow())
.toMat(Sink.foreach(s -> LOGGER.debug("client received message: '{}'", s)), Keep.right());
CompletableFuture<SourceQueueWithComplete<String>> futureClientSideSourceQueue = new CompletableFuture<>();
Source<Message, SourceQueueWithComplete<String>> source
= Source.<String>queue(0, OverflowStrategy.backpressure())
.alsoToMat(Sink.foreach(s -> LOGGER.debug("client sending '{}'", s)), Keep.left())
.map(s -> (Message) TextMessage.create(s))
.mapMaterializedValue(sourceQueue -> {
futureClientSideSourceQueue.complete(sourceQueue);
return sourceQueue;
});
Flow<Message, Message, CompletionStage<Done>> clientFlow = Flow.fromSinkAndSourceCoupledMat(sink, source, Keep.left());
WebSocketRequest webSocketRequest
= WebSocketRequest.create("ws://localhost:9999");
Pair<CompletionStage<WebSocketUpgradeResponse>, CompletionStage<Done>> clientPair
= http.singleWebSocketRequest(webSocketRequest,
clientFlow,
materializer);
CompletionStage<WebSocketUpgradeResponse> clientSideUpgradeResponse = clientPair.first();
CompletionStage<Done> clientSideConnected = clientSideUpgradeResponse.thenApply(upgrade -> {
if (upgrade.response().status().equals(StatusCodes.SWITCHING_PROTOCOLS)) {
return Done.getInstance();
} else {
throw new RuntimeException("Connection failed: " + upgrade.response().status());
}
});
CompletionStage<Done> clientSideClosed = clientPair.second();
clientSideConnected.thenAccept(done -> {
LOGGER.debug("Client connected");
LOGGER.debug("Terminating all connections with a 1 second hard deadline");
CompletionStage<HttpTerminated> onceAllConnectionsTerminated
= serverBinding.terminate(Duration.ofSeconds(1));
serverBinding.whenTerminated().thenAccept(terminated -> {
LOGGER.debug("whenTerminated() -> terminated");
});
onceAllConnectionsTerminated.toCompletableFuture()
.thenAccept(terminated -> {
LOGGER.debug("All connections terminated.");
try {
LOGGER.debug("Waiting 5 seconds before sending messages from the client to the terminated server.");
Thread.sleep(5000); // wait 5 seconds
SourceQueueWithComplete<String> queue = futureClientSideSourceQueue.get();
queue.offer("message 1");
queue.offer("message 2");
queue.offer("message 3");
queue.offer("message 4");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
});
try {
LOGGER.debug("Waiting 15 seconds for client connection to close.");
clientSideClosed
.thenAccept(done -> LOGGER.debug("Client connection closed"))
.toCompletableFuture()
.get(15, TimeUnit.SECONDS); // wait for connection to close
} catch (InterruptedException e) {
LOGGER.error("Interrupted waiting for client connection to close", e);
} catch (TimeoutException e) {
LOGGER.error("Timeout waiting for client connection to close", e);
}
}
}
I get the following output:
[DEBUG] [10/25/2018 12:14:18.763] [main] [EventStream(akka://default)] logger log1-Logging$DefaultLogger started
[DEBUG] [10/25/2018 12:14:18.780] [main] [EventStream(akka://default)] Default Loggers started
[DEBUG] [10/25/2018 12:14:19.297] [main] [AkkaSSLConfig(akka://default)] Initializing AkkaSSLConfig extension...
[DEBUG] [10/25/2018 12:14:19.306] [main] [AkkaSSLConfig(akka://default)] buildHostnameVerifier: created hostname verifier: com.typesafe.sslconfig.ssl.DefaultHostnameVerifier#31c7528f
[DEBUG] [10/25/2018 12:14:20.343] [default-akka.actor.default-dispatcher-6]
[akka://default/system/IO-TCP/selectors/$a/0] Successfully bound to /127.0.0.1:9999
2018-10-25 12:14:20 DEBUG AkkaWebSocketServerTerminateTest:130 - Waiting 15 seconds for client connection to close.
[DEBUG] [10/25/2018 12:14:20.479] [default-akka.actor.default-dispatcher-4]
[akka://default/system/IO-TCP/selectors/$a/1] Resolving localhost before connecting
[DEBUG] [10/25/2018 12:14:20.496] [default-akka.actor.default-dispatcher-6]
[akka://default/system/IO-DNS] Resolution request for localhost from Actor[akka://default/system/IO-TCP/selectors/$a/1#1366663132]
[DEBUG] [10/25/2018 12:14:20.536] [default-akka.actor.default-dispatcher-6]
[akka://default/system/IO-TCP/selectors/$a/1] Attempting connection to [localhost/127.0.0.1:9999]
[DEBUG] [10/25/2018 12:14:20.537] [default-akka.actor.default-dispatcher-4]
[akka://default/system/IO-TCP/selectors/$a/0] New connection accepted
[DEBUG] [10/25/2018 12:14:20.538] [default-akka.actor.default-dispatcher-6]
[akka://default/system/IO-TCP/selectors/$a/1] Connection established to [localhost:9999]
2018-10-25 12:14:20 DEBUG AkkaWebSocketServerTerminateTest:104 - Client connected
2018-10-25 12:14:20 DEBUG AkkaWebSocketServerTerminateTest:105 - Terminating all connections with a 1 second hard deadline
[DEBUG] [10/25/2018 12:14:20.642] [default-akka.actor.default-dispatcher-13]
[akka://default/system/IO-TCP/selectors/$a/0] Unbinding endpoint /127.0.0.1:9999
[DEBUG] [10/25/2018 12:14:20.643] [default-akka.actor.default-dispatcher-13]
[akka://default/system/IO-TCP/selectors/$a/0] Unbound endpoint /127.0.0.1:9999, stopping listener
2018-10-25 12:14:20 DEBUG AkkaWebSocketServerTerminateTest:108 - whenTerminated() -> terminated
2018-10-25 12:14:20 DEBUG AkkaWebSocketServerTerminateTest:112 - All connections terminated.
2018-10-25 12:14:20 DEBUG AkkaWebSocketServerTerminateTest:114 - Waiting 5 seconds before sending messages from the client to the terminated server.
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:76 - client sending 'message 1'
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:76 - client sending 'message 2'
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:76 - client sending 'message 3'
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:76 - client sending 'message 4'
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:53 - handling message 1
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:53 - handling message 2
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:70 - client received message: 'handled message 1'
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:53 - handling message 3
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:53 - handling message 4
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:70 - client received message: 'handled message 2'
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:70 - client received message: 'handled message 3'
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:70 - client received message: 'handled message 4'
2018-10-25 12:14:35 ERROR AkkaWebSocketServerTerminateTest:138 - Timeout waiting for client connection to close
I think explicit termination is not possible but you can set akka.http.server.idle-timeout according to note from their documentation
Inactive WebSocket connections will be dropped according to the idle-timeout settings. In case you need to keep inactive connections alive, you can either tweak your idle-timeout or inject ‘keep-alive’ messages regularly.
I am currently working on a google cloud project in free trial mode. I have cron job to fetch the data from a data vendor and store it in the data store. I wrote the code to fetch the data couple of weeks ago and it was all working fine but all of sudden , i started receiving error "DeadlineExceededError: The overall deadline for responding to the HTTP request was exceeded" for last two days. I believe cron job is supposed to timeout only after 60 minutes any idea why i am getting the error?.
cron task
def run():
try:
config = cron.config
actual_data_source = config['xxx']['xxxx']
original_data_source = actual_data_source
company_list = cron.rest_client.load(config, "companies", '')
if not company_list:
logging.info("Company list is empty")
return "Ok"
for row in company_list:
company_repository.save(row,original_data_source, actual_data_source)
return "OK"
Repository code
def save( dto, org_ds , act_dp):
try:
key = 'FIN/%s' % (dto['ticker'])
company = CompanyInfo(id=key)
company.stock_code = key
company.ticker = dto['ticker']
company.name = dto['name']
company.original_data_source = org_ds
company.actual_data_provider = act_dp
company.put()
return company
except Exception:
logging.exception("company_repository: error occurred saving the company
record ")
raise
RestClient
def load(config, resource, filter):
try:
username = config['xxxx']['xxxx']
password = config['xxxx']['xxxx']
headers = {"Authorization": "Basic %s" % base64.b64encode(username + ":"
+ password)}
if filter:
from_date = filter['from']
to_date = filter['to']
ticker = filter['ticker']
start_date = datetime.strptime(from_date, '%Y%m%d').strftime("%Y-%m-%d")
end_date = datetime.strptime(to_date, '%Y%m%d').strftime("%Y-%m-%d")
current_page = 1
data = []
while True:
if (filter):
url = config['xxxx']["endpoints"][resource] % (ticker, current_page, start_date, end_date)
else:
url = config['xxxx']["endpoints"][resource] % (current_page)
response = urlfetch.fetch(
url=url,
deadline=60,
method=urlfetch.GET,
headers=headers,
follow_redirects=False,
)
if response.status_code != 200:
logging.error("xxxx GET received status code %d!" % (response.status_code))
logging.error("error happend for url: %s with headers %s", url, headers)
return 'Sorry, xxxx API request failed', 500
db = json.loads(response.content)
if not db['data']:
break
data.extend(db['data'])
if db['total_pages'] == current_page:
break
current_page += 1
return data
except Exception:
logging.exception("Error occured with xxxx API request")
raise
I'm guessing this is the same question as this, but now with more code:
DeadlineExceededError: The overall deadline for responding to the HTTP request was exceeded
I modified your code to write to the database after each urlfetch. If there are more pages, then it relaunches itself in a deferred task, which should be well before the 10 minute timeout.
Uncaught exceptions in a deferred task cause it to retry, so be mindful of that.
It was unclear to me how actual_data_source & original_data_source worked, but I think you should be able to modify that part.
crontask
def run(current_page=0):
try:
config = cron.config
actual_data_source = config['xxx']['xxxx']
original_data_source = actual_data_source
data, more = cron.rest_client.load(config, "companies", '', current_page)
for row in data:
company_repository.save(row, original_data_source, actual_data_source)
# fetch the rest
if more:
deferred.defer(run, current_page + 1)
except Exception as e:
logging.exception("run() experienced an error: %s" % e)
RestClient
def load(config, resource, filter, current_page):
try:
username = config['xxxx']['xxxx']
password = config['xxxx']['xxxx']
headers = {"Authorization": "Basic %s" % base64.b64encode(username + ":"
+ password)}
if filter:
from_date = filter['from']
to_date = filter['to']
ticker = filter['ticker']
start_date = datetime.strptime(from_date, '%Y%m%d').strftime("%Y-%m-%d")
end_date = datetime.strptime(to_date, '%Y%m%d').strftime("%Y-%m-%d")
url = config['xxxx']["endpoints"][resource] % (ticker, current_page, start_date, end_date)
else:
url = config['xxxx']["endpoints"][resource] % (current_page)
response = urlfetch.fetch(
url=url,
deadline=60,
method=urlfetch.GET,
headers=headers,
follow_redirects=False,
)
if response.status_code != 200:
logging.error("xxxx GET received status code %d!" % (response.status_code))
logging.error("error happend for url: %s with headers %s", url, headers)
return [], False
db = json.loads(response.content)
return db['data'], (db['total_pages'] != current_page)
except Exception as e:
logging.exception("Error occured with xxxx API request: %s" % e)
return [], False
I would prefer to write this as a comment, but I need more reputation to do that.
What happens when you run the actual data fetch directly instead of
through the cron job?
Have you tried measuring a time delta from the start to the end of
the job?
Has the number of companies being retrieved increased dramatically?
You appear to be doing some form of stock quote aggregation - is it
possible that the provider has started blocking you?
I'm trying to connect to my local sql server database at my home network using the code that I found from this site Querying SQL Server with Google Apps Script via JDBC about 3 years ago which was marked being correct. However, I get the error message "We're sorry, a server error occurred. Please wait a bit and try again.". This error is from line 2 where the connection string is defined. I retried several times, but I always get the same error. When I searched this error, it seems like it could be too many things and I was not able to find any answers for my issue. Thanks.
This was the code that was marked being correct:
function readAzure() {
var conn = Jdbc.getConnection("jdbc:sqlserver://XYZ.database.windows.net:1433;databaseName=MYDATABSENAME","USERNAME","PASSWORD");
var stmt = conn.createStatement();
var rs = stmt.executeQuery("select * from helloworld");
var doc = SpreadsheetApp.create('azure');
var cell = doc.getRange('a1');
var row = 0;
while(rs.next()) {
cell.offset(row, 0).setValue(rs.getString(1));
cell.offset(row, 1).setValue(rs.getString(2));
row++;
}
rs.close();
stmt.close();
conn.close();
}
I also found another connection string code and when I try this format I get the same error.
var conn = Jdbc.getConnection("jdbc:sqlserver://IP-address:1433;" + "databaseName=DBName;user=username;password=password;");
Based on this documentation, you need to ensure that your database accepts connections from any of Apps Script's IP addresses. These are the address ranges you'll need to whitelist.
64.18.0.0 - 64.18.15.255
64.233.160.0 - 64.233.191.255
66.102.0.0 - 66.102.15.255
66.249.80.0 - 66.249.95.255
72.14.192.0 - 72.14.255.255
74.125.0.0 - 74.125.255.255
173.194.0.0 - 173.194.255.255
207.126.144.0 - 207.126.159.255
209.85.128.0 - 209.85.255.255
216.239.32.0 - 216.239.63.255
Also, from the documentation link above, there is a sample code that you can follow to set up external database via JDBC.
Here is a code that demonstrates how to write a single record to the database as well as a batch of 500 records.
// Replace the variables in this block with real values.
var address = 'database_IP_address';
var user = 'user_name';
var userPwd = 'user_password';
var db = 'database_name';
var dbUrl = 'jdbc:mysql://' + address + '/' + db;
// Write one row of data to a table.
function writeOneRecord() {
var conn = Jdbc.getConnection(dbUrl, user, userPwd);
var stmt = conn.prepareStatement('INSERT INTO entries '
+ '(guestName, content) values (?, ?)');
stmt.setString(1, 'First Guest');
stmt.setString(2, 'Hello, world');
stmt.execute();
}
// Write 500 rows of data to a table in a single batch.
function writeManyRecords() {
var conn = Jdbc.getConnection(dbUrl, user, userPwd);
conn.setAutoCommit(false);
var start = new Date();
var stmt = conn.prepareStatement('INSERT INTO entries '
+ '(guestName, content) values (?, ?)');
for (var i = 0; i < 500; i++) {
stmt.setString(1, 'Name ' + i);
stmt.setString(2, 'Hello, world ' + i);
stmt.addBatch();
}
var batch = stmt.executeBatch();
conn.commit();
conn.close();
var end = new Date();
Logger.log('Time elapsed: %sms for %s rows.', end - start, batch.length);
}
For more information, just read through to this documentation and check this thread and related SO question.
I'm trying to upload file on one of my Azure containers
this is one of my request send with ajax:
headers: Object
Authorization: "SharedKey MYACCOUNT:ENC_KEY"
Content-Type: "application/octet-stream"
data: File
x-ms-blob-type: "BlockBlob"
x-ms-date: "Mon, 19 Oct 2015 13:54:53 GMT"
x-ms-version: "2009-09-19"
type: "PUT"
url: "https://MYACCOUNT.blob.core.windows.net/data-test"
for the ENC_KEY I use :
authorizationHeader =
compute: (options, xhrOptions) ->
sig = #_computeSignature(options, xhrOptions)
result = 'SharedKey ' + options.storageAccount + ':' + sig
result
_computeSignature: (options, xhrOptions) ->
sigString = #_getSignatureString(options, xhrOptions)
key = CryptoJS.enc.Base64.parse(options.primaryKey)
hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key)
hmac.update sigString
hash = hmac.finalize()
result = hash.toString(CryptoJS.enc.Base64)
result
any ideas?
EDIT :
all code for authorizationHeader ->
https://gist.github.com/F4Ke/88debcede3b7e2312b11
2)
ERROR RESPONSE :
PUT https://MYACCOUNT.blob.core.windows.net/data-test 403 (Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.)
3)
#_getCanonicalizedHeadersString
itemcreation.coffee:232 x-ms-blob-type:BlockBlob
x-ms-date:Mon, 19 Oct 2015 14:33:15 GMT
x-ms-version:2009-09-19
#_getSignatureString
PUT
application/octet-stream
x-ms-blob-type:BlockBlob
x-ms-date:Mon, 19 Oct 2015 14:35:19 GMT
x-ms-version:2009-09-19
/MYACCOUNT/MYACCOUNT.blob.core.windows.net/data-test
The REST documentation describing how to sign a message can be found here. You can also take a look at the node shared key implementation hhere - might also help you...
I have a test written in Jasmine test runner:
it("Expect 'due date' to be 14 days from today", function () {
var dateNow = new Date();
scope.dateOfService(dateNow);
expect(scope.DueDate == new Date(dateNow.setDate(dateNow.getDate() + 14))).toBeTruthy();
});
However this always returns false? Is there something I'm doing incorrect?
To 'debug' this I also ran:
expect(scope.DueDate).toBe(new Date(dateNow.setDate(dateNow.getDate() + 14)));
And this returns:
Expected Date(Tue Nov 11 2014 08:20:23 GMT+0000 (GMT Standard Time)) to be Date(Tue Nov 11 2014 08:20:23 GMT+0000 (GMT Standard Time))
var dateNow = new Date();
scope.dateOfService(dateNow);
var dateReturned = new Date(scope.DueDate);
var dateAdd = new Date(dateNow.setDate(dateNow.getDate() + 14));
expect(dateReturned).toEqual(dateAdd);
The main point was to ensure it was .toEqual rather than .toBe.