pokeplot 0.2.0beta
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +4 -0
- data/LICENSE.txt +621 -0
- data/README.md +99 -0
- data/Rakefile +9 -0
- data/bin/pokeplot +223 -0
- data/lib/pokeplot/api.rb +109 -0
- data/lib/pokeplot/database.rb +22 -0
- data/lib/pokeplot/helpers/array.rb +5 -0
- data/lib/pokeplot/helpers/cell_ids.rb +14 -0
- data/lib/pokeplot/helpers/math.rb +20 -0
- data/lib/pokeplot/helpers/time.rb +15 -0
- data/lib/pokeplot/miner.rb +241 -0
- data/lib/pokeplot/pushbullet.rb +41 -0
- data/lib/pokeplot/socket.rb +65 -0
- data/lib/pokeplot/version.rb +3 -0
- data/lib/pokeplot/web/canvasjs.min.js +555 -0
- data/lib/pokeplot/web/index.html +158 -0
- data/lib/pokeplot/web.rb +26 -0
- data/lib/pokeplot.rb +7 -0
- data/spec/api_spec.rb +148 -0
- data/spec/database_spec.rb +33 -0
- data/spec/miner_spec.rb +110 -0
- data/spec/pushbullet_spec.rb +31 -0
- data/spec/socket_spec.rb +45 -0
- data/spec/spec_helper.rb +115 -0
- data/spec/web_spec.rb +18 -0
- metadata +240 -0
@@ -0,0 +1,158 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
7
|
+
<title>Document</title>
|
8
|
+
<style>
|
9
|
+
* {
|
10
|
+
margin: 0;
|
11
|
+
padding: 0;
|
12
|
+
}
|
13
|
+
h1, h2 {
|
14
|
+
width: 100%;
|
15
|
+
text-align: center;
|
16
|
+
margin: auto;
|
17
|
+
}
|
18
|
+
</style>
|
19
|
+
</head>
|
20
|
+
<body>
|
21
|
+
<h1>Pokeplot Visualizations</h1>
|
22
|
+
<h2>Pokemon Frequency</h2>
|
23
|
+
<hr / />
|
24
|
+
<div id="frequency_pie" style="height: 50vh; width: 98vw;"></div>
|
25
|
+
<div id="least_frequent" style="height: 45vh; width: 48vw;display: inline-block;"></div>
|
26
|
+
<div id="most_frequent" style="height: 45vh; width: 48vw; display: inline-block;"></div>
|
27
|
+
<!-- <br />
|
28
|
+
<span id="num_unique">Number of unique pokemon found: <b>0</b></span> -->
|
29
|
+
<h2>Spawn Points</h2>
|
30
|
+
<hr />
|
31
|
+
<div id="spawn_frequency" style="height: 50vh; width: 98vw;"></div>
|
32
|
+
<script src="canvasjs.min.js"></script>
|
33
|
+
<script>
|
34
|
+
var pokemon = {};
|
35
|
+
var spawns = {};
|
36
|
+
function socket() {
|
37
|
+
websocket = new WebSocket("ws://" + document.location.hostname + ":9090");
|
38
|
+
|
39
|
+
websocket.onmessage = function(evt) {
|
40
|
+
data = JSON.parse(evt.data);
|
41
|
+
if (data.type == "count") {
|
42
|
+
data = data.data;
|
43
|
+
for (var mon in data) {
|
44
|
+
pokemon.hasOwnProperty(mon) ? (pokemon[mon] += data[mon]) : (pokemon[mon] = data[mon]);
|
45
|
+
}
|
46
|
+
}else if (data.type == "pokemon") {
|
47
|
+
data = data.data;
|
48
|
+
pokemon.hasOwnProperty(data.pokemon) ? (pokemon[data.pokemon] += 1) : (pokemon[data.pokemon] = 1);
|
49
|
+
spawns.hasOwnProperty(data.spawn_point_id) ? (spawns[data.spawn_point_id] += 1) : (spawns[data.spawn_point_id] = 1);
|
50
|
+
}else if (data.type == "spawn_points") {
|
51
|
+
data = data.data;
|
52
|
+
for (var spawn in data) {
|
53
|
+
spawns.hasOwnProperty(spawn) ? (spawns[spawn] += data[spawn]) : (spawns[spawn] = data[spawn]);
|
54
|
+
}
|
55
|
+
}
|
56
|
+
updateCharts();
|
57
|
+
};
|
58
|
+
websocket.onclose = function(evt) {
|
59
|
+
alert("Connection to server lost");
|
60
|
+
}
|
61
|
+
}
|
62
|
+
socket();
|
63
|
+
|
64
|
+
function freqPie(content, total) {
|
65
|
+
var chart = new CanvasJS.Chart("frequency_pie",
|
66
|
+
{
|
67
|
+
theme: "theme2",
|
68
|
+
exportFileName: "pokemon_freq_pie",
|
69
|
+
exportEnabled: true,
|
70
|
+
title:{
|
71
|
+
text: "Pokemon Frequency (Total: " + total + ")"
|
72
|
+
},
|
73
|
+
data: [
|
74
|
+
{
|
75
|
+
type: "pie",
|
76
|
+
toolTipContent: "{label}: {y} - #percent %",
|
77
|
+
indexLabel: "{label}: #percent%",
|
78
|
+
indexLabelFontSize: 15,
|
79
|
+
indexLabelFontColor: "darkslategray",
|
80
|
+
indexLabelPlacement: "outside",
|
81
|
+
indexLabelFormatter: function(e) { if (e.percent >= 1) { return e.dataPoint.label + ": " + e.percent.toFixed(2) + "%"} },
|
82
|
+
dataPoints: content
|
83
|
+
}
|
84
|
+
]
|
85
|
+
});
|
86
|
+
chart.render();
|
87
|
+
}
|
88
|
+
function freqBar(contentSlice, title, total, id) {
|
89
|
+
var chart = new CanvasJS.Chart(id,
|
90
|
+
{
|
91
|
+
theme: 'theme2',
|
92
|
+
exportFileName: "pokemon_most_freq",
|
93
|
+
exportEnabled: true,
|
94
|
+
title: {text: title},
|
95
|
+
axisY: {title: "Number of encounters"},
|
96
|
+
axisX: {labelFontSize: 10, interval: 1},
|
97
|
+
data: [
|
98
|
+
{
|
99
|
+
type: "bar",
|
100
|
+
dataPoints: contentSlice
|
101
|
+
}
|
102
|
+
]
|
103
|
+
});
|
104
|
+
chart.render();
|
105
|
+
}
|
106
|
+
function spawnFreq(content, total) {
|
107
|
+
var chart = new CanvasJS.Chart("spawn_frequency",
|
108
|
+
{
|
109
|
+
theme: 'theme2',
|
110
|
+
exportFileName: "spawn_frequency",
|
111
|
+
exportEnabled: true,
|
112
|
+
title: {text: "Spawn Point Frequency (Total: " + total + ")"},
|
113
|
+
axisY: {title: "Number of encounters"},
|
114
|
+
axisX: {labelFormatter: function (e) { return "" }, title: "Spawn Point Id (Hover)"},
|
115
|
+
data: [
|
116
|
+
{
|
117
|
+
type: "bar",
|
118
|
+
dataPoints: content
|
119
|
+
}
|
120
|
+
]
|
121
|
+
});
|
122
|
+
chart.render();
|
123
|
+
}
|
124
|
+
|
125
|
+
function unique(n) {
|
126
|
+
document.getElementById('num_unique').innerHTML = "Number of unique pokemon found: <b>" + n + "</b>";
|
127
|
+
}
|
128
|
+
|
129
|
+
function updateCharts() {
|
130
|
+
dataPokemon1 = [];
|
131
|
+
dataSpawns1 = [];
|
132
|
+
totalEncounters = 0;
|
133
|
+
totalSpawns = 0;
|
134
|
+
|
135
|
+
for (var mon in pokemon) {
|
136
|
+
totalEncounters += pokemon[mon]
|
137
|
+
dataPokemon1.push({label: mon, y: pokemon[mon]});
|
138
|
+
}
|
139
|
+
dataPokemon1.sort(function(a,b) { return b.y - a.y });
|
140
|
+
|
141
|
+
for (var spawn in spawns) {
|
142
|
+
totalSpawns += 1
|
143
|
+
dataSpawns1.push({label: spawn, y: spawns[spawn]});
|
144
|
+
}
|
145
|
+
dataSpawns1 = dataSpawns1.slice(0, 31)
|
146
|
+
dataSpawns1.sort(function(a,b) { return b.y - a.y });
|
147
|
+
|
148
|
+
freqPie(dataPokemon1, totalEncounters);
|
149
|
+
freqBar(dataPokemon1.slice(-20), "Least Frequent Pokemon", totalEncounters, "least_frequent");
|
150
|
+
freqBar(dataPokemon1.slice(0, 21), "Most Frequent Pokemon", totalEncounters, "most_frequent");
|
151
|
+
//unique(Object.keys(pokemon).length);
|
152
|
+
spawnFreq(dataSpawns1.slice(0, 31), totalSpawns);
|
153
|
+
}
|
154
|
+
|
155
|
+
|
156
|
+
</script>
|
157
|
+
</body>
|
158
|
+
</html>
|
data/lib/pokeplot/web.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'puma'
|
3
|
+
|
4
|
+
module Pokeplot
|
5
|
+
class Web < Sinatra::Base
|
6
|
+
include Database
|
7
|
+
|
8
|
+
set :bind, '0.0.0.0'
|
9
|
+
set :port, 5001
|
10
|
+
set :server, :puma
|
11
|
+
set :traps, false
|
12
|
+
set :static, true
|
13
|
+
set :public_folder, __dir__ + "/web"
|
14
|
+
|
15
|
+
def self.config(host = '0.0.0.0', port = 5001)
|
16
|
+
@@db = Database.mongo
|
17
|
+
set :bind, host
|
18
|
+
set :port, port
|
19
|
+
end
|
20
|
+
|
21
|
+
get '/' do
|
22
|
+
send_file File.join(settings.public_folder, 'index.html')
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/pokeplot.rb
ADDED
data/spec/api_spec.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'pokeplot/api'
|
2
|
+
require 'pokeplot/helpers/cell_ids'
|
3
|
+
require 'poke-api/errors'
|
4
|
+
|
5
|
+
describe Pokeplot::API do
|
6
|
+
|
7
|
+
subject(:api) {
|
8
|
+
|
9
|
+
api = double()
|
10
|
+
|
11
|
+
allow(api).to receive_messages(:lat => 40.7829, :lng => 73.9654)
|
12
|
+
allow(api).to receive_messages([
|
13
|
+
:activate_signature,
|
14
|
+
:lat=,
|
15
|
+
:lng=,
|
16
|
+
:login,
|
17
|
+
:get_player,
|
18
|
+
:get_hatched_eggs,
|
19
|
+
:get_inventory,
|
20
|
+
:check_awarded_badges,
|
21
|
+
:download_settings,
|
22
|
+
:get_map_objects,
|
23
|
+
:call
|
24
|
+
])
|
25
|
+
|
26
|
+
expect(Poke::API::Logging).to receive(:log_level=)
|
27
|
+
expect(Poke::API::Client).to receive(:new).and_return(api).at_least(:once)
|
28
|
+
|
29
|
+
described_class.new(40.7829, 73.9654, '', false)
|
30
|
+
}
|
31
|
+
|
32
|
+
describe ".new" do
|
33
|
+
it "should create an instance of Pokeplot::API" do
|
34
|
+
expect(api).to be_a(Pokeplot::API)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#login" do
|
39
|
+
context "successful" do
|
40
|
+
it "should login" do
|
41
|
+
client = api.instance_variable_get(:@api)
|
42
|
+
expect(client).to receive(:login).with(instance_of(String), instance_of(String), /(google|ptc)/)
|
43
|
+
expect(api).to receive(:loop).and_yield
|
44
|
+
api.login('', '', 'ptc')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "error" do
|
49
|
+
error = StandardError.new
|
50
|
+
error.set_backtrace("test error")
|
51
|
+
error.exception
|
52
|
+
error = Poke::API::Errors::UnknownProtoFault.new(error)
|
53
|
+
|
54
|
+
it "should sleep for 5 seconds" do
|
55
|
+
client = api.instance_variable_get(:@api)
|
56
|
+
expect(client).to receive(:login).with(instance_of(String), instance_of(String), /(google|ptc)/).and_raise(error)
|
57
|
+
expect(api).to receive(:loop).and_yield
|
58
|
+
expect(api).to receive(:sleep).with(5)
|
59
|
+
api.login('','','ptc')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#set_location" do
|
65
|
+
it "should set lat/lng" do
|
66
|
+
client = api.instance_variable_get(:@api)
|
67
|
+
expect(client).to receive(:lat=)
|
68
|
+
expect(client).to receive(:lng=)
|
69
|
+
api.set_location(40.7829, 73.9654)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#lat=" do
|
74
|
+
it "should set lat" do
|
75
|
+
client = api.instance_variable_get(:@api)
|
76
|
+
expect(client).to receive(:lat=)
|
77
|
+
api.lat = 40.7829
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "#lng=" do
|
82
|
+
it "should set lng" do
|
83
|
+
client = api.instance_variable_get(:@api)
|
84
|
+
expect(client).to receive(:lng=)
|
85
|
+
api.lng = 73.9654
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#map_heartbeat" do
|
90
|
+
|
91
|
+
before(:example) do |example|
|
92
|
+
data = [
|
93
|
+
{:status_code => 1},
|
94
|
+
{:status_code => 2},
|
95
|
+
{:status_code => 102},
|
96
|
+
{:status_code => 000}
|
97
|
+
]
|
98
|
+
|
99
|
+
client = api.instance_variable_get(:@api)
|
100
|
+
expect(client).to receive_messages([:get_player, :get_hatched_eggs, :get_inventory, :check_awarded_badges, :download_settings, :get_map_objects])
|
101
|
+
expect(api).to receive(:loop).and_yield
|
102
|
+
|
103
|
+
call = double()
|
104
|
+
if example.metadata[:data].is_a?(Integer)
|
105
|
+
allow(call).to receive(:response).and_return(data[example.metadata[:data]])
|
106
|
+
else
|
107
|
+
error = StandardError.new
|
108
|
+
error.set_backtrace("test error")
|
109
|
+
error.exception
|
110
|
+
error = Poke::API::Errors::UnknownProtoFault.new(error)
|
111
|
+
allow(call).to receive(:response).and_raise(error)
|
112
|
+
end
|
113
|
+
expect(client).to receive(:call).and_return(call)
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
context "response succesful" do
|
118
|
+
it "should respond with a status code of 1", :data => 0 do
|
119
|
+
expect(api.map_heartbeat).to eq({:status_code => 1})
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "response error" do
|
124
|
+
it "could fail and rety", :data => 1 do
|
125
|
+
expect(api).to receive(:basic_request)
|
126
|
+
api.map_heartbeat
|
127
|
+
end
|
128
|
+
|
129
|
+
it "could re-login and retry", :data => 2 do
|
130
|
+
expect(api).to receive(:login)
|
131
|
+
api.map_heartbeat
|
132
|
+
end
|
133
|
+
|
134
|
+
it "could sleep and retry", :data => 3 do
|
135
|
+
expect(api).to receive(:sleep)
|
136
|
+
api.map_heartbeat
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context "proto error" do
|
141
|
+
it "should re-login and retry" do
|
142
|
+
expect(api).to receive(:login)
|
143
|
+
api.map_heartbeat
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'pokeplot/database'
|
2
|
+
|
3
|
+
describe Pokeplot::Database do
|
4
|
+
|
5
|
+
describe '.mongo' do
|
6
|
+
it "should create a new mongo client" do
|
7
|
+
expect(Mongo::Client).to receive(:new)
|
8
|
+
described_class.mongo
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '.mongo_host=' do
|
13
|
+
it "should set mongo_host class variable" do
|
14
|
+
expect(
|
15
|
+
described_class.class_eval('@@mongo_host')
|
16
|
+
).to eq('127.0.0.1:27017')
|
17
|
+
|
18
|
+
described_class.mongo_host = '127.0.0.1:30300'
|
19
|
+
|
20
|
+
expect(
|
21
|
+
described_class.class_eval('@@mongo_host')
|
22
|
+
).to eq('127.0.0.1:30300')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '.mongo_monitor' do
|
27
|
+
it "Should subscribe a class to mongo global monitor" do
|
28
|
+
expect(Mongo::Monitoring::Global).to receive(:subscribe).with(Mongo::Monitoring::COMMAND, anything)
|
29
|
+
described_class.mongo_monitor(described_class)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/spec/miner_spec.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'pokeplot/miner'
|
2
|
+
require 'pokeplot/database'
|
3
|
+
require 'pokeplot/helpers/math'
|
4
|
+
|
5
|
+
describe Pokeplot::Miner do
|
6
|
+
|
7
|
+
subject(:miner) {
|
8
|
+
db = double()
|
9
|
+
allow(db).to receive(:find).and_return(db)
|
10
|
+
allow(db).to receive(:insert_one)
|
11
|
+
allow(db).to receive(:count).and_return(0,0,1,1) #test unique and non unique
|
12
|
+
allow(db).to receive(:[]).and_return(db)
|
13
|
+
|
14
|
+
accounts = [{'username' => '', 'password' => '', 'provider' => 'ptc'}]
|
15
|
+
|
16
|
+
expect(Pokeplot::Database).to receive(:mongo).at_least(:once).and_return(db)
|
17
|
+
m = described_class.new(accounts, 74.3, 43.2, 1, 0, true, true, false, '', false, false)
|
18
|
+
|
19
|
+
return m
|
20
|
+
}
|
21
|
+
|
22
|
+
describe ".new" do
|
23
|
+
it "should create an instance of Pokeplot::Miner" do
|
24
|
+
expect(miner).to be_a(Pokeplot::Miner)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#start" do
|
29
|
+
|
30
|
+
data2 = {:GET_MAP_OBJECTS => {:status => :SUCCESS, :map_cells => [{:s2_cell_id => 10, :current_timestamp_ms => 400, :forts => [{:id => 1, :enabled => false, :latitude => 0, :longitude => 0, :type => :GYM}], :wild_pokemons => [{:time_till_hidden_ms => 0, :encounter_id => 1, :latitude => 3, :longitude => 4, :spawn_point_id => 5, :pokemon_data => {:pokemon_id => :PIDGEY}}]}]}}
|
31
|
+
data1 = {:GET_MAP_OBJECTS => {:status => :SUCCESS, :map_cells => [{:forts => [], :wild_pokemons => [{:time_till_hidden_ms => -1, :pokemon_data => {:pokemon_id => :WEEDLE}}]}]}}
|
32
|
+
|
33
|
+
context "single thread" do
|
34
|
+
it "should create a miner thread" do
|
35
|
+
#To test normal and negative
|
36
|
+
|
37
|
+
api = double()
|
38
|
+
allow(api).to receive(:set_location).and_return(api)
|
39
|
+
allow(api).to receive(:map_heartbeat).and_return(data1, data2)
|
40
|
+
allow(api).to receive(:login).with(instance_of(String), instance_of(String), /(google|ptc)/)
|
41
|
+
|
42
|
+
expect(Thread).to receive(:new).and_yield
|
43
|
+
expect(miner).to receive(:loop).and_yield
|
44
|
+
expect(Pokeplot::API).to receive(:new).and_return(api)
|
45
|
+
miner.start
|
46
|
+
miner.stop
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "multiple threads" do
|
51
|
+
it "should create more than 1 thread" do
|
52
|
+
miner
|
53
|
+
api = double()
|
54
|
+
allow(api).to receive(:set_location).and_return(api)
|
55
|
+
allow(api).to receive(:map_heartbeat).and_return(data1, data2)
|
56
|
+
allow(api).to receive(:login).with(instance_of(String), instance_of(String), /(google|ptc)/).at_least(:once)
|
57
|
+
|
58
|
+
accounts = [{'username' => '', 'password' => '', 'provider' => 'ptc'}]
|
59
|
+
m = described_class.new(accounts, 74.3, 43.2, 1, 0, true, true, true, '', false, false)
|
60
|
+
|
61
|
+
expect(Thread).to receive(:new).at_least(:twice).and_yield
|
62
|
+
allow(m).to receive(:loop).and_yield
|
63
|
+
expect(Pokeplot::API).to receive(:new).and_return(api)
|
64
|
+
|
65
|
+
m.start
|
66
|
+
m.stop
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#stop" do
|
72
|
+
it "should delete the miner thread" do
|
73
|
+
miner.start
|
74
|
+
expect(miner.miner).not_to be(nil)
|
75
|
+
miner.stop
|
76
|
+
expect(miner.miner).to be(nil)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "#get_all_coords" do
|
81
|
+
it "should create an array of coords" do
|
82
|
+
expect(miner.send('get_all_coords')).to all(have_key(:lat) & have_key(:lng))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#parse_map_objects" do
|
87
|
+
context "successful status" do
|
88
|
+
let(:data) { {:GET_MAP_OBJECTS => {:status => :SUCCESS, :map_cells => [{:s2_cell_id => 10, :current_timestamp_ms => 400, :forts => [{:id => 1, :enabled => false, :latitude => 0, :longitude => 0, :type => :GYM}], :wild_pokemons => [{:time_till_hidden_ms => 0, :encounter_id => 1, :latitude => 3, :longitude => 4, :spawn_point_id => 5, :pokemon_data => {:pokemon_id => :PIDGEY}}]}]}} }
|
89
|
+
it "should save encounters and forts" do
|
90
|
+
|
91
|
+
expect(miner.db).to receive(:find).with(instance_of(Hash)).twice
|
92
|
+
expect(miner.db).to receive(:insert_one).with(instance_of(Hash)).twice
|
93
|
+
expect(miner.db).to receive(:count).twice
|
94
|
+
expect(miner.db).to receive(:[]).with(instance_of(Symbol)).exactly(4).times
|
95
|
+
|
96
|
+
miner.send('parse_map_objects', data)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "unsuccessful status" do
|
101
|
+
let(:data) { {:GET_MAP_OBJECTS => {:status => :ERROR}} }
|
102
|
+
it "should log an error" do
|
103
|
+
expect(
|
104
|
+
miner.send('parse_map_objects', data)
|
105
|
+
).to eq(nil)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'pokeplot/pushbullet'
|
2
|
+
|
3
|
+
describe Pokeplot::Pushbullet do
|
4
|
+
|
5
|
+
describe ".new" do
|
6
|
+
it "should create a washbullet client" do
|
7
|
+
expect(Washbullet::Client).to receive(:new).with(instance_of(String))
|
8
|
+
described_class.new('apikey')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#started" do
|
13
|
+
it "should sent a notification for pokemon" do
|
14
|
+
device = double()
|
15
|
+
allow(device).to receive_messages(:body => device, :[] => "identifier")
|
16
|
+
client = double()
|
17
|
+
allow(client).to receive(:devices).and_return([device])
|
18
|
+
expect(client).to receive(:push_note).with(instance_of(Hash))
|
19
|
+
|
20
|
+
mongo = double()
|
21
|
+
allow(mongo).to receive(:command).and_return({'insert' => 'encounters', 'documents' => [{:pokemon => "SEEL", :current_timestamp_ms => 400, :latitude => 0, :longitude => 0, :time_till_hidden_ms => 10}]})
|
22
|
+
|
23
|
+
expect(Washbullet::Client).to receive(:new).with(instance_of(String))
|
24
|
+
|
25
|
+
push = described_class.new("apikey", ['SEEL'])
|
26
|
+
push.instance_variable_set(:@client, client)
|
27
|
+
push.started(mongo)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/spec/socket_spec.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'pokeplot/socket'
|
2
|
+
require 'pokeplot/database'
|
3
|
+
require 'eventmachine'
|
4
|
+
|
5
|
+
describe Pokeplot::Socket do
|
6
|
+
|
7
|
+
describe ".new" do
|
8
|
+
data = [{:pokemon => 1, :spawn_point_id => 1}, {:pokemon => 2, :spawn_point_id => 2}, {:pokemon => 1, :spawn_point_id => 1}]
|
9
|
+
|
10
|
+
it "should start a websocket" do
|
11
|
+
ws = double()
|
12
|
+
allow(ws).to receive(:onopen).and_yield(ws)
|
13
|
+
allow(ws).to receive(:onclose).and_yield
|
14
|
+
allow(ws).to receive(:onmessage)
|
15
|
+
allow(ws).to receive(:send)
|
16
|
+
|
17
|
+
db = double()
|
18
|
+
allow(db).to receive(:[]).and_return(db)
|
19
|
+
allow(db).to receive(:find).and_return(data)
|
20
|
+
|
21
|
+
expect(Thread).to receive(:new).and_yield
|
22
|
+
expect(Pokeplot::Database).to receive(:mongo).and_return(db)
|
23
|
+
expect(EventMachine).to receive(:run).and_yield
|
24
|
+
expect(EM::WebSocket).to receive(:run).and_yield(ws)
|
25
|
+
described_class.new
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#started" do
|
30
|
+
data = {'insert' => 'encounters', 'documents' => ["pokemon data"]}
|
31
|
+
|
32
|
+
it "should send data to the clients" do
|
33
|
+
client = double()
|
34
|
+
allow(client).to receive(:send)
|
35
|
+
|
36
|
+
event = double(:command => data)
|
37
|
+
|
38
|
+
expect(Thread).to receive(:new).and_return(nil)
|
39
|
+
socket = described_class.new
|
40
|
+
socket.instance_variable_set(:@clients, [client])
|
41
|
+
socket.started(event)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require "codeclimate-test-reporter"
|
2
|
+
CodeClimate::TestReporter.start
|
3
|
+
|
4
|
+
require 'rack/test'
|
5
|
+
ENV['RACK_ENV'] = 'test'
|
6
|
+
|
7
|
+
module RSpecMixin
|
8
|
+
include Rack::Test::Methods
|
9
|
+
def app() described_class end
|
10
|
+
end
|
11
|
+
|
12
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
13
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
14
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
15
|
+
# this file to always be loaded, without a need to explicitly require it in any
|
16
|
+
# files.
|
17
|
+
#
|
18
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
19
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
20
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
21
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
22
|
+
# a separate helper file that requires the additional dependencies and performs
|
23
|
+
# the additional setup, and require it from the spec files that actually need
|
24
|
+
# it.
|
25
|
+
#
|
26
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
27
|
+
# users commonly want.
|
28
|
+
#
|
29
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
30
|
+
RSpec.configure do |config|
|
31
|
+
config.include RSpecMixin
|
32
|
+
# rspec-expectations config goes here. You can use an alternate
|
33
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
34
|
+
# assertions if you prefer.
|
35
|
+
config.expect_with :rspec do |expectations|
|
36
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
37
|
+
# and `failure_message` of custom matchers include text for helper methods
|
38
|
+
# defined using `chain`, e.g.:
|
39
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
40
|
+
# # => "be bigger than 2 and smaller than 4"
|
41
|
+
# ...rather than:
|
42
|
+
# # => "be bigger than 2"
|
43
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
44
|
+
end
|
45
|
+
|
46
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
47
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
48
|
+
config.mock_with :rspec do |mocks|
|
49
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
50
|
+
# a real object. This is generally recommended, and will default to
|
51
|
+
# `true` in RSpec 4.
|
52
|
+
mocks.verify_partial_doubles = true
|
53
|
+
end
|
54
|
+
|
55
|
+
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
|
56
|
+
# have no way to turn it off -- the option exists only for backwards
|
57
|
+
# compatibility in RSpec 3). It causes shared context metadata to be
|
58
|
+
# inherited by the metadata hash of host groups and examples, rather than
|
59
|
+
# triggering implicit auto-inclusion in groups with matching metadata.
|
60
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
61
|
+
|
62
|
+
# The settings below are suggested to provide a good initial experience
|
63
|
+
# with RSpec, but feel free to customize to your heart's content.
|
64
|
+
=begin
|
65
|
+
# This allows you to limit a spec run to individual examples or groups
|
66
|
+
# you care about by tagging them with `:focus` metadata. When nothing
|
67
|
+
# is tagged with `:focus`, all examples get run. RSpec also provides
|
68
|
+
# aliases for `it`, `describe`, and `context` that include `:focus`
|
69
|
+
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
|
70
|
+
config.filter_run_when_matching :focus
|
71
|
+
|
72
|
+
# Allows RSpec to persist some state between runs in order to support
|
73
|
+
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
74
|
+
# you configure your source control system to ignore this file.
|
75
|
+
config.example_status_persistence_file_path = "spec/examples.txt"
|
76
|
+
|
77
|
+
# Limits the available syntax to the non-monkey patched syntax that is
|
78
|
+
# recommended. For more details, see:
|
79
|
+
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
|
80
|
+
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
81
|
+
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
|
82
|
+
config.disable_monkey_patching!
|
83
|
+
|
84
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
85
|
+
# be too noisy due to issues in dependencies.
|
86
|
+
config.warnings = true
|
87
|
+
|
88
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
89
|
+
# file, and it's useful to allow more verbose output when running an
|
90
|
+
# individual spec file.
|
91
|
+
if config.files_to_run.one?
|
92
|
+
# Use the documentation formatter for detailed output,
|
93
|
+
# unless a formatter has already been configured
|
94
|
+
# (e.g. via a command-line flag).
|
95
|
+
config.default_formatter = 'doc'
|
96
|
+
end
|
97
|
+
|
98
|
+
# Print the 10 slowest examples and example groups at the
|
99
|
+
# end of the spec run, to help surface which specs are running
|
100
|
+
# particularly slow.
|
101
|
+
config.profile_examples = 10
|
102
|
+
|
103
|
+
# Run specs in random order to surface order dependencies. If you find an
|
104
|
+
# order dependency and want to debug it, you can fix the order by providing
|
105
|
+
# the seed, which is printed after each run.
|
106
|
+
# --seed 1234
|
107
|
+
config.order = :random
|
108
|
+
|
109
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
110
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
111
|
+
# test failures related to randomization by passing the same `--seed` value
|
112
|
+
# as the one that triggered the failure.
|
113
|
+
Kernel.srand config.seed
|
114
|
+
=end
|
115
|
+
end
|