1 | _ = require 'lodash'
|
2 | shmock = require 'shmock'
|
3 | MessageController = require '../src/message-controller'
|
4 |
|
5 | API_HOST_PORT = 0xdead
|
6 | TRIGGER_HOST_PORT = 0xbeef
|
7 | SLACK_HOST_PORT = 0xf00d
|
8 |
|
9 | describe 'Canary', ->
|
10 | @timeout 30000
|
11 |
|
12 | before ->
|
13 | @DateMock =
|
14 | now: => @time or 0
|
15 | setTime: (@time) =>
|
16 | inc: (delta) => @time += delta
|
17 |
|
18 | @startTime = Date.now()
|
19 | @DateMock.setTime @startTime
|
20 |
|
21 | process.env.OCTOBLU_CANARY_UUID = 'canary_uuid'
|
22 | process.env.OCTOBLU_CANARY_TOKEN = 'canary_token'
|
23 | process.env.OCTOBLU_API_HOST = 'http://localhost:' + API_HOST_PORT
|
24 | process.env.OCTOBLU_TRIGGER_HOST = 'http://localhost:' + TRIGGER_HOST_PORT
|
25 | process.env.SLACK_CHANNEL_URL = 'http://localhost:' + SLACK_HOST_PORT + '/slackTest'
|
26 |
|
27 | @CANARY_RESTART_FLOWS_MAX_TIME = process.env.CANARY_RESTART_FLOWS_MAX_TIME = 1000*60
|
28 | @CANARY_UPDATE_INTERVAL = process.env.CANARY_UPDATE_INTERVAL = 1000*120
|
29 | @CANARY_HEALTH_CHECK_MAX_DIFF = process.env.CANARY_HEALTH_CHECK_MAX_DIFF = 100
|
30 | @CANARY_DATA_HISTORY_SIZE = process.env.CANARY_DATA_HISTORY_SIZE = 3
|
31 |
|
32 | @apiHost = shmock API_HOST_PORT
|
33 | @triggerHost = shmock TRIGGER_HOST_PORT
|
34 | @slackHost = shmock SLACK_HOST_PORT
|
35 |
|
36 | @flows = [
|
37 | flowId: "flow-a"
|
38 | name: "trigger flow a"
|
39 | activated: true
|
40 | nodes: [
|
41 | id: "trigger-flow-a"
|
42 | type: "operation:trigger"
|
43 | ]
|
44 | ,
|
45 | flowId: "flow-b"
|
46 | name: "trigger flow b"
|
47 | nodes: [
|
48 | id: "trigger-flow-b"
|
49 | type: "operation:trigger"
|
50 | ]
|
51 | ,
|
52 | flowId: "flow-c"
|
53 | name: "something something"
|
54 | ]
|
55 |
|
56 | @sut = new MessageController Date: @DateMock
|
57 |
|
58 | @resetFlowTime = (name, time) =>
|
59 | (@sut.canary.stats.getFlows())[name] = {messageTime:[time]}
|
60 |
|
61 | after (done) ->
|
62 | @apiHost.close =>
|
63 | @triggerHost.close =>
|
64 | @slackHost.close done
|
65 |
|
66 | describe '-> canary', ->
|
67 | it 'should have a message endpoint', ->
|
68 | expect(@sut.postMessage).to.exist
|
69 |
|
70 | it 'should have a stats endpoint', ->
|
71 | expect(@sut.getStats).to.exist
|
72 |
|
73 | it 'should have a passing endpoint', ->
|
74 | expect(@sut.getPassing).to.exist
|
75 |
|
76 | it 'should have a postTriggers function', ->
|
77 | expect(@sut.canary.postTriggers).to.exist
|
78 |
|
79 | it 'should have a function for getting current stats', ->
|
80 | expect(@sut.canary.getStats).to.exist
|
81 |
|
82 | it 'should have an initial failing state', ->
|
83 | expect(@sut.canary.getPassing().passing).to.equal false
|
84 |
|
85 | describe 'when startAllFlows is called', ->
|
86 | before (done) ->
|
87 | @getFlows = @apiHost.get('/api/flows').reply(200, @flows)
|
88 | @startFlowA = @apiHost.post('/api/flows/flow-a/instance').reply(201)
|
89 | @startFlowB = @apiHost.post('/api/flows/flow-b/instance').reply(201)
|
90 | @startFlowC = @apiHost.post('/api/flows/flow-c/instance').reply(201)
|
91 | @sut.canary.startAllFlows =>
|
92 | @DateMock.inc @CANARY_UPDATE_INTERVAL
|
93 | done()
|
94 |
|
95 | it 'should have fetched our flows and started them', ->
|
96 | expect(@getFlows.isDone).to.be.true
|
97 | expect(@startFlowA.isDone).to.be.true
|
98 | expect(@startFlowB.isDone).to.be.true
|
99 | expect(@startFlowC.isDone).to.be.true
|
100 |
|
101 | it 'should be in a passing state', ->
|
102 | expect(@sut.canary.getPassing().passing).to.equal true
|
103 |
|
104 | describe 'when one of the flows hasn\'t been messaged in awhile', ->
|
105 | before ->
|
106 | @resetFlowTime 'flow-a', @DateMock.now() - @CANARY_UPDATE_INTERVAL*2
|
107 |
|
108 | it 'should be in a failing state', ->
|
109 | expect(@sut.canary.getPassing().passing).to.equal false
|
110 |
|
111 | describe 'and we message them a bunch', ->
|
112 | before ->
|
113 | messageCanary = =>
|
114 | @DateMock.inc @CANARY_UPDATE_INTERVAL
|
115 | @sut.postMessage {body:fromUuid:'flow-a'}, {end:=>}
|
116 | @sut.postMessage {body:fromUuid:'flow-b'}, {end:=>}
|
117 | @sut.postMessage {body:fromUuid:'flow-c'}, {end:=>}
|
118 | _.times @CANARY_DATA_HISTORY_SIZE+1, messageCanary
|
119 |
|
120 | it 'should be in a passing state', ->
|
121 |
|
122 | expect(@sut.canary.getPassing().passing).to.equal true
|
123 |
|
124 | describe 'when postTriggers is called', ->
|
125 | before (done) ->
|
126 | @triggerAPost = @triggerHost.post('/flows/flow-a/triggers/trigger-flow-a').reply(201)
|
127 | @triggerBPost = @triggerHost.post('/flows/flow-b/triggers/trigger-flow-b').reply(201)
|
128 | @sut.canary.postTriggers done
|
129 |
|
130 | it 'should have posted to both triggers', ->
|
131 | expect(@triggerAPost.isDone).to.be.true
|
132 | expect(@triggerBPost.isDone).to.be.true
|
133 |
|
134 | describe 'when one of the other flows hasn\'t been messaged in awhile', ->
|
135 | before ->
|
136 | @resetFlowTime 'flow-c', @DateMock.now() - @CANARY_UPDATE_INTERVAL*2
|
137 |
|
138 | it 'should be in a failing state', ->
|
139 |
|
140 | expect(@sut.canary.getPassing().passing).to.equal false
|
141 |
|
142 | xdescribe 'when processUpdateInterval is called', ->
|
143 | before (done) ->
|
144 | @getFlows = @apiHost.get('/api/flows').reply(200, @flows)
|
145 | @startFlowC = @apiHost.post('/api/flows/flow-c/instance').reply(201)
|
146 | @triggerAPost = @triggerHost.post('/flows/flow-a/triggers/trigger-flow-a').reply(201)
|
147 | @triggerBPost = @triggerHost.post('/flows/flow-b/triggers/trigger-flow-b').reply(201)
|
148 | @slackPost = @slackHost.post('/slackTest').reply(200)
|
149 |
|
150 | @sut.canary.processUpdateInterval done
|
151 |
|
152 | it 'should have fetched the flows, restarted the failed flow, and posted to triggers', ->
|
153 | expect(@getFlows.isDone).to.be.true
|
154 | expect(@startFlowC.isDone).to.be.true
|
155 | expect(@triggerAPost.isDone).to.be.true
|
156 | expect(@triggerBPost.isDone).to.be.true
|
157 | expect(@slackPost.isDone).to.be.true
|
158 |
|
159 | it 'should have no errors in stats', ->
|
160 |
|
161 | expect(_.isEmpty(@sut.canary.getStats().errors)).to.be.true
|
162 |
|
163 | xdescribe 'when processUpdateInterval and everything errors', ->
|
164 | before (done) ->
|
165 | @resetFlowTime 'flow-c', @DateMock.now() - @CANARY_UPDATE_INTERVAL*2
|
166 | @getFlows = @apiHost.get('/api/flows').reply(401, @flows)
|
167 | @startFlowC = @apiHost.post('/api/flows/flow-c/instance').reply(401)
|
168 | @triggerAPost = @triggerHost.post('/flows/flow-a/triggers/trigger-flow-a').reply(401)
|
169 | @triggerBPost = @triggerHost.post('/flows/flow-b/triggers/trigger-flow-b').reply(401)
|
170 | @slackPost = @slackHost.post('/slackTest').reply(200)
|
171 |
|
172 | @sut.canary.processUpdateInterval done
|
173 |
|
174 | it 'should have tried to fetch the flows, restart the failed flow, and post to triggers', ->
|
175 | expect(@getFlows.isDone).to.be.true
|
176 | expect(@startFlowC.isDone).to.be.true
|
177 | expect(@triggerAPost.isDone).to.be.true
|
178 | expect(@triggerBPost.isDone).to.be.true
|
179 | expect(@slackPost.isDone).to.be.true
|
180 |
|
181 | it 'should have errors in stats', ->
|
182 |
|
183 | expect(@sut.canary.getStats().errors?.length).to.equal 4
|
184 |
|
185 | describe 'when one of the flows is messaged too often', ->
|
186 | before ->
|
187 | @resetFlowTime 'flow-a', @DateMock.now()
|
188 | @resetFlowTime 'flow-b', @DateMock.now()
|
189 | @resetFlowTime 'flow-c', @DateMock.now()
|
190 | delete @sut.canary.stats.errors
|
191 |
|
192 | it 'should initialy be in a passing state', ->
|
193 | expect(@sut.canary.getPassing().passing).to.equal true
|
194 |
|
195 | describe 'and we message a flow once', ->
|
196 | before ->
|
197 | @DateMock.inc @CANARY_UPDATE_INTERVAL - (@CANARY_HEALTH_CHECK_MAX_DIFF*1.1)
|
198 | @sut.postMessage {body:fromUuid:'flow-a'}, {end:=>}
|
199 |
|
200 | it 'should be in a failing state', ->
|
201 |
|
202 | expect(@sut.canary.getPassing().passing).to.equal false
|