Merge branch 'master' into global_messages
commit
f60a56c894
|
@ -2,15 +2,18 @@ class SendNervesHubInfoJob < ApplicationJob
|
||||||
queue_as :default
|
queue_as :default
|
||||||
|
|
||||||
def perform(device_id:, serial_number:, tags:)
|
def perform(device_id:, serial_number:, tags:)
|
||||||
device = Device.find(device_id)
|
device = Device.find(device_id)
|
||||||
resp_data = NervesHub.create_or_update(serial_number, tags)
|
resp_data = NervesHub.maybe_create_or_update(serial_number, tags)
|
||||||
certs = NervesHub.sign_device(resp_data.fetch(:identifier))
|
unless resp_data # Probably has bad tags if nil
|
||||||
|
return
|
||||||
|
end
|
||||||
|
certs = NervesHub.sign_device(resp_data.fetch(:identifier))
|
||||||
Transport.current.amqp_send(certs.to_json, device_id, "nerves_hub")
|
Transport.current.amqp_send(certs.to_json, device_id, "nerves_hub")
|
||||||
rescue => error
|
rescue => error
|
||||||
NervesHub.report_problem({ error: error,
|
NervesHub.report_problem({ error: error,
|
||||||
device_id: device_id,
|
device_id: device_id,
|
||||||
serial_number: serial_number,
|
serial_number: serial_number,
|
||||||
tags: tags, })
|
tags: tags })
|
||||||
raise error
|
raise error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
require "net/http"
|
require "net/http"
|
||||||
require "openssl"
|
require "openssl"
|
||||||
require "base64"
|
require "base64"
|
||||||
|
|
||||||
class NervesHub
|
class NervesHub
|
||||||
class NervesHubHTTPError < StandardError; end
|
class NervesHubHTTPError < StandardError; end
|
||||||
|
|
||||||
# There is a lot of configuration available in this class to support:
|
# There is a lot of configuration available in this class to support:
|
||||||
# * Self Hosters
|
# * Self Hosters
|
||||||
# * Running a local instance of Nerves-Hub
|
# * Running a local instance of Nerves-Hub
|
||||||
|
@ -41,27 +43,27 @@ class NervesHub
|
||||||
# via AMQP.
|
# via AMQP.
|
||||||
# * FarmBot burns that cert into internal storage on it's SD card.
|
# * FarmBot burns that cert into internal storage on it's SD card.
|
||||||
|
|
||||||
NERVES_HUB_HOST = ENV.fetch("NERVES_HUB_HOST") { "api.nerves-hub.org" }
|
NERVES_HUB_HOST = ENV.fetch("NERVES_HUB_HOST") { "api.nerves-hub.org" }
|
||||||
NERVES_HUB_PORT = ENV.fetch("NERVES_HUB_PORT") { 443 }
|
NERVES_HUB_PORT = ENV.fetch("NERVES_HUB_PORT") { 443 }
|
||||||
NERVES_HUB_ORG = ENV.fetch("NERVES_HUB_ORG") { "farmbot" }
|
NERVES_HUB_ORG = ENV.fetch("NERVES_HUB_ORG") { "farmbot" }
|
||||||
NERVES_HUB_BASE_URL = "https://#{NERVES_HUB_HOST}:#{NERVES_HUB_PORT}"
|
NERVES_HUB_BASE_URL = "https://#{NERVES_HUB_HOST}:#{NERVES_HUB_PORT}"
|
||||||
NERVES_HUB_URI = URI.parse(NERVES_HUB_BASE_URL)
|
NERVES_HUB_URI = URI.parse(NERVES_HUB_BASE_URL)
|
||||||
|
|
||||||
# Locations of where files _may_ exist.
|
# Locations of where files _may_ exist.
|
||||||
NERVES_HUB_CERT_PATH = "nerves_hub_cert.#{Rails.env}.pem"
|
NERVES_HUB_CERT_PATH = "nerves_hub_cert.#{Rails.env}.pem"
|
||||||
NERVES_HUB_KEY_PATH = "nerves_hub_key.#{Rails.env}.pem"
|
NERVES_HUB_KEY_PATH = "nerves_hub_key.#{Rails.env}.pem"
|
||||||
NERVES_HUB_CA_PATH = "nerves_hub_ca.#{Rails.env}.pem"
|
NERVES_HUB_CA_PATH = "nerves_hub_ca.#{Rails.env}.pem"
|
||||||
|
|
||||||
# This file is for loading the CA from ENV.
|
# This file is for loading the CA from ENV.
|
||||||
# net/http doesn't support loading this as a X509::Certificate
|
# net/http doesn't support loading this as a X509::Certificate
|
||||||
# So it needs to be written to a path.
|
# So it needs to be written to a path.
|
||||||
NERVES_HUB_CA_HACK = "/tmp/nerves_hub_ca.#{Rails.env}.pem"
|
NERVES_HUB_CA_HACK = "/tmp/nerves_hub_ca.#{Rails.env}.pem"
|
||||||
NERVES_HUB_ERROR = "NervesHub request failed: %s: %s"
|
NERVES_HUB_ERROR = "NervesHub request failed: %s: %s"
|
||||||
COLON = ":"
|
COLON = ":"
|
||||||
BAD_TAG = "A device sent a malformed tag"
|
BAD_TAG = "A device sent a malformed tag"
|
||||||
|
|
||||||
# HEADERS for HTTP requests to NervesHub
|
# HEADERS for HTTP requests to NervesHub
|
||||||
HEADERS = {"Content-Type" => "application/json"}
|
HEADERS = { "Content-Type" => "application/json" }
|
||||||
DEFAULT_HTTP = Net::HTTP.new(NERVES_HUB_URI.host, NERVES_HUB_URI.port)
|
DEFAULT_HTTP = Net::HTTP.new(NERVES_HUB_URI.host, NERVES_HUB_URI.port)
|
||||||
|
|
||||||
# Raises an exception for when NervesHub API requests fail.
|
# Raises an exception for when NervesHub API requests fail.
|
||||||
|
@ -70,7 +72,7 @@ class NervesHub
|
||||||
end
|
end
|
||||||
|
|
||||||
APPLICATION = "application"
|
APPLICATION = "application"
|
||||||
CHANNEL = "channel"
|
CHANNEL = "channel"
|
||||||
|
|
||||||
def self.update_channel(serial_number, channel)
|
def self.update_channel(serial_number, channel)
|
||||||
dev = device(serial_number)
|
dev = device(serial_number)
|
||||||
|
@ -80,19 +82,19 @@ class NervesHub
|
||||||
# NEVER DUPLICATE TAG PREFIXES (thing before COLON). Must be unique!
|
# NEVER DUPLICATE TAG PREFIXES (thing before COLON). Must be unique!
|
||||||
tag_map = dev.fetch(:tags).map { |x| x.split(COLON) }.to_h
|
tag_map = dev.fetch(:tags).map { |x| x.split(COLON) }.to_h
|
||||||
tag_map[CHANNEL] = channel
|
tag_map[CHANNEL] = channel
|
||||||
next_tags = tag_map.to_a.map { |x| x.join(COLON) }
|
next_tags = tag_map.to_a.map { |x| x.join(COLON) }
|
||||||
update(serial_number, next_tags)
|
update(serial_number, next_tags)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks if a device exists in NervesHub
|
# Checks if a device exists in NervesHub
|
||||||
# if it does -> does a PUT request updating the tags.
|
# if it does -> does a PUT request updating the tags.
|
||||||
# if it does not -> does a POST request creating the device with given tags.
|
# if it does not -> does a POST request creating the device with given tags.
|
||||||
def self.create_or_update(serial_number, tags)
|
def self.maybe_create_or_update(serial_number, tags)
|
||||||
# Hash | nil
|
# Hash | nil
|
||||||
current_nerves_hub_device = device(serial_number)
|
current_nerves_hub_device = device(serial_number)
|
||||||
|
|
||||||
# It's really hard to debug malformed tags; Catch them here:
|
# It's really hard to debug malformed tags; Catch them here:
|
||||||
if tags.detect{|x| !x.include?(COLON) }
|
if tags.detect { |x| !x.include?(COLON) }
|
||||||
report_problem(error: BAD_TAG, serial_number: serial_number, tags: tags)
|
report_problem(error: BAD_TAG, serial_number: serial_number, tags: tags)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -119,7 +121,7 @@ class NervesHub
|
||||||
|
|
||||||
# PUT request to a device to update it's tags.
|
# PUT request to a device to update it's tags.
|
||||||
def self.update(serial_number, tags)
|
def self.update(serial_number, tags)
|
||||||
data = {tags: tags}
|
data = { tags: tags }
|
||||||
resp = conn.put(device_path(serial_number), data.to_json, HEADERS)
|
resp = conn.put(device_path(serial_number), data.to_json, HEADERS)
|
||||||
bad_http(resp.code, resp.body) if resp.code != "201"
|
bad_http(resp.code, resp.body) if resp.code != "201"
|
||||||
JSON(resp.body)["data"].deep_symbolize_keys
|
JSON(resp.body)["data"].deep_symbolize_keys
|
||||||
|
@ -129,8 +131,8 @@ class NervesHub
|
||||||
# to identify the ENV that FarmBotOS is running in.
|
# to identify the ENV that FarmBotOS is running in.
|
||||||
def self.new_device(serial_number, tags)
|
def self.new_device(serial_number, tags)
|
||||||
data = { description: "farmbot-#{serial_number}",
|
data = { description: "farmbot-#{serial_number}",
|
||||||
identifier: serial_number,
|
identifier: serial_number,
|
||||||
tags: tags }
|
tags: tags }
|
||||||
resp = conn.post(devices_path, data.to_json, HEADERS)
|
resp = conn.post(devices_path, data.to_json, HEADERS)
|
||||||
bad_http(resp.code, resp.body) if resp.code != "201"
|
bad_http(resp.code, resp.body) if resp.code != "201"
|
||||||
JSON(resp.body)["data"].deep_symbolize_keys
|
JSON(resp.body)["data"].deep_symbolize_keys
|
||||||
|
@ -146,14 +148,14 @@ class NervesHub
|
||||||
csr_safe = Base64.strict_encode64(csr.to_pem)
|
csr_safe = Base64.strict_encode64(csr.to_pem)
|
||||||
|
|
||||||
data = { identifier: serial_number,
|
data = { identifier: serial_number,
|
||||||
csr: csr_safe, }
|
csr: csr_safe }
|
||||||
resp = conn.post(device_sign_path(serial_number), data.to_json, HEADERS)
|
resp = conn.post(device_sign_path(serial_number), data.to_json, HEADERS)
|
||||||
bad_http(resp.code, resp.body) if resp.code != "200"
|
bad_http(resp.code, resp.body) if resp.code != "200"
|
||||||
cert = JSON(resp.body)["data"].deep_symbolize_keys[:cert]
|
cert = JSON(resp.body)["data"].deep_symbolize_keys[:cert]
|
||||||
|
|
||||||
return { cert: Base64.strict_encode64(cert),
|
return { cert: Base64.strict_encode64(cert),
|
||||||
csr: csr_safe,
|
csr: csr_safe,
|
||||||
key: key_safe, }
|
key: key_safe }
|
||||||
end
|
end
|
||||||
|
|
||||||
# Is the NervesHub module configured.
|
# Is the NervesHub module configured.
|
||||||
|
@ -163,16 +165,16 @@ class NervesHub
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.set_conn(obj = DEFAULT_HTTP)
|
def self.set_conn(obj = DEFAULT_HTTP)
|
||||||
@conn = obj
|
@conn = obj
|
||||||
# Setting the contents of this
|
# Setting the contents of this
|
||||||
# in the CA store doesn't work for some reason?
|
# in the CA store doesn't work for some reason?
|
||||||
@conn.ca_file = self.current_ca_file
|
@conn.ca_file = self.current_ca_file
|
||||||
# Don't think this is absolutely needed.
|
# Don't think this is absolutely needed.
|
||||||
@conn.cert_store = nil
|
@conn.cert_store = nil
|
||||||
@conn = obj
|
@conn = obj
|
||||||
@conn.use_ssl = true
|
@conn.use_ssl = true
|
||||||
@conn.cert = current_cert
|
@conn.cert = current_cert
|
||||||
@conn.key = current_key
|
@conn.key = current_key
|
||||||
@conn
|
@conn
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -181,7 +183,7 @@ class NervesHub
|
||||||
(active? && !@conn) ? set_conn : @conn
|
(active? && !@conn) ? set_conn : @conn
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Helper for making requests to a device url on NervesHub
|
# Helper for making requests to a device url on NervesHub
|
||||||
def self.devices_path(*chunks)
|
def self.devices_path(*chunks)
|
||||||
|
@ -216,7 +218,7 @@ private
|
||||||
request = OpenSSL::X509::Request.new
|
request = OpenSSL::X509::Request.new
|
||||||
request.version = 0
|
request.version = 0
|
||||||
request.subject = OpenSSL::X509::Name.new([
|
request.subject = OpenSSL::X509::Name.new([
|
||||||
['O', options[:organization], OpenSSL::ASN1::UTF8STRING],
|
["O", options[:organization], OpenSSL::ASN1::UTF8STRING],
|
||||||
])
|
])
|
||||||
request.public_key = real_public_key(key)
|
request.public_key = real_public_key(key)
|
||||||
request.sign(key, OpenSSL::Digest::SHA1.new)
|
request.sign(key, OpenSSL::Digest::SHA1.new)
|
||||||
|
@ -236,7 +238,7 @@ private
|
||||||
|
|
||||||
# Cert for authenticating Farmbot API (NOT FARMBOT OS) to NervesHub
|
# Cert for authenticating Farmbot API (NOT FARMBOT OS) to NervesHub
|
||||||
def self.try_env_cert
|
def self.try_env_cert
|
||||||
OpenSSL::X509::Certificate.new(ENV['NERVES_HUB_CERT']) if ENV['NERVES_HUB_CERT']
|
OpenSSL::X509::Certificate.new(ENV["NERVES_HUB_CERT"]) if ENV["NERVES_HUB_CERT"]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Cert for authenticating Farmbot API (NOT FARMBOT OS) to NervesHub
|
# Cert for authenticating Farmbot API (NOT FARMBOT OS) to NervesHub
|
||||||
|
@ -249,7 +251,7 @@ private
|
||||||
|
|
||||||
# Cert for authenticating Farmbot API (NOT FARMBOT OS) to NervesHub
|
# Cert for authenticating Farmbot API (NOT FARMBOT OS) to NervesHub
|
||||||
def self.try_env_key
|
def self.try_env_key
|
||||||
OpenSSL::PKey::EC.new(ENV['NERVES_HUB_KEY']) if ENV['NERVES_HUB_KEY']
|
OpenSSL::PKey::EC.new(ENV["NERVES_HUB_KEY"]) if ENV["NERVES_HUB_KEY"]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Private Key for authenticating Farmbot API (NOT FARMBOT OS) to NervesHub
|
# Private Key for authenticating Farmbot API (NOT FARMBOT OS) to NervesHub
|
||||||
|
@ -276,9 +278,9 @@ private
|
||||||
# loading a file from the filesystem.
|
# loading a file from the filesystem.
|
||||||
# https://stackoverflow.com/questions/36993208/how-to-enumerate-through-multiple-certificates-in-a-bundle
|
# https://stackoverflow.com/questions/36993208/how-to-enumerate-through-multiple-certificates-in-a-bundle
|
||||||
def self.try_env_ca_file
|
def self.try_env_ca_file
|
||||||
if ENV['NERVES_HUB_CA']
|
if ENV["NERVES_HUB_CA"]
|
||||||
file = File.open(NERVES_HUB_CA_HACK, 'w')
|
file = File.open(NERVES_HUB_CA_HACK, "w")
|
||||||
file.write(ENV['NERVES_HUB_CA'])
|
file.write(ENV["NERVES_HUB_CA"])
|
||||||
file.close
|
file.close
|
||||||
NERVES_HUB_CA_HACK
|
NERVES_HUB_CA_HACK
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,18 +4,32 @@ describe SendNervesHubInfoJob do
|
||||||
let(:device) { FactoryBot.create(:device) }
|
let(:device) { FactoryBot.create(:device) }
|
||||||
|
|
||||||
it "handles failure" do
|
it "handles failure" do
|
||||||
params = { device_id: device.id,
|
params = { device_id: device.id,
|
||||||
serial_number: "xyz",
|
serial_number: "xyz",
|
||||||
tags: [],
|
tags: [],
|
||||||
error: StandardError.new("Hello!"), }
|
error: StandardError.new("Hello!") }
|
||||||
not_work = \
|
not_work = receive(:maybe_create_or_update)
|
||||||
receive(:create_or_update).with(any_args).and_raise(params.fetch(:error))
|
.with(any_args)
|
||||||
|
.and_raise(params.fetch(:error))
|
||||||
expect(NervesHub).to not_work
|
expect(NervesHub).to not_work
|
||||||
old_logger = ActiveJob::Base.logger
|
old_logger = ActiveJob::Base.logger
|
||||||
ActiveJob::Base.logger = Logger.new(nil)
|
ActiveJob::Base.logger = Logger.new(nil)
|
||||||
expect do
|
expect do
|
||||||
SendNervesHubInfoJob.perform_now(**params.except(:error))
|
SendNervesHubInfoJob.perform_now(**params.except(:error))
|
||||||
end.to raise_error(params.fetch(:error))
|
end.to raise_error(params.fetch(:error))
|
||||||
ActiveJob::Base.logger = old_logger
|
ActiveJob::Base.logger = old_logger
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "returns early if create/update is nil" do
|
||||||
|
params = { device_id: device.id,
|
||||||
|
serial_number: "xyz",
|
||||||
|
tags: [],
|
||||||
|
error: StandardError.new("Hello!") }
|
||||||
|
return_nil = receive(:maybe_create_or_update)
|
||||||
|
.with(any_args)
|
||||||
|
.and_return(nil)
|
||||||
|
expect(NervesHub).to return_nil
|
||||||
|
expect(NervesHub).not_to receive(:sign_device)
|
||||||
|
SendNervesHubInfoJob.perform_now(**params.except(:error))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,14 +2,15 @@ require "spec_helper"
|
||||||
|
|
||||||
describe NervesHub do
|
describe NervesHub do
|
||||||
def stub_connection
|
def stub_connection
|
||||||
double(SecureRandom.hex.first(6), :ca_file= => nil,
|
double(SecureRandom.hex.first(6), :ca_file= => nil,
|
||||||
:cert_store => nil,
|
:cert_store => nil,
|
||||||
:cert_store= => nil,
|
:cert_store= => nil,
|
||||||
:use_ssl => nil,
|
:use_ssl => nil,
|
||||||
:use_ssl= => nil,
|
:use_ssl= => nil,
|
||||||
:cert= => nil,
|
:cert= => nil,
|
||||||
:key= => nil)
|
:key= => nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
before(:each) { NervesHub.set_conn(stub_connection) }
|
before(:each) { NervesHub.set_conn(stub_connection) }
|
||||||
|
|
||||||
# reset to default.
|
# reset to default.
|
||||||
|
@ -20,14 +21,13 @@ describe NervesHub do
|
||||||
|
|
||||||
it "generates HTTP failure messages" do
|
it "generates HTTP failure messages" do
|
||||||
status = "800"
|
status = "800"
|
||||||
msg = "failed to reticulate splines."
|
msg = "failed to reticulate splines."
|
||||||
expect { NervesHub.bad_http(status, msg) }
|
expect { NervesHub.bad_http(status, msg) }.to raise_error(NervesHub::NervesHubHTTPError)
|
||||||
.to raise_error(NervesHub::NervesHubHTTPError)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "generates URL paths" do
|
it "generates URL paths" do
|
||||||
expect(NervesHub.devices_path).to eq "/orgs/farmbot/devices"
|
expect(NervesHub.devices_path).to eq "/orgs/farmbot/devices"
|
||||||
expect(NervesHub.device_path("foo")).to eq "/orgs/farmbot/devices/foo"
|
expect(NervesHub.device_path("foo")).to eq "/orgs/farmbot/devices/foo"
|
||||||
expect(NervesHub.device_sign_path(123)).to eq "/orgs/farmbot/devices/123/certificates/sign"
|
expect(NervesHub.device_sign_path(123)).to eq "/orgs/farmbot/devices/123/certificates/sign"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -53,21 +53,20 @@ describe NervesHub do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "handles failed updates to a device" do
|
it "handles failed updates to a device" do
|
||||||
resp = StubResp.new("500", { "data" => { } }.to_json)
|
resp = StubResp.new("500", { "data" => {} }.to_json)
|
||||||
expected_args = [NervesHub.device_path(ser),
|
expected_args = [NervesHub.device_path(ser),
|
||||||
{"tags":["foo"]}.to_json,
|
{ "tags": ["foo"] }.to_json,
|
||||||
NervesHub::HEADERS]
|
NervesHub::HEADERS]
|
||||||
|
|
||||||
expect(NervesHub.conn).to receive(:put).with(*expected_args).and_return(resp)
|
expect(NervesHub.conn).to receive(:put).with(*expected_args).and_return(resp)
|
||||||
|
|
||||||
expect { NervesHub.update(ser, ["foo"]) }
|
expect { NervesHub.update(ser, ["foo"]) }.to raise_error(NervesHub::NervesHubHTTPError)
|
||||||
.to raise_error(NervesHub::NervesHubHTTPError)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "updates the device via REST" do
|
it "updates the device via REST" do
|
||||||
resp = StubResp.new("201", { "data" => {x: "y"} }.to_json)
|
resp = StubResp.new("201", { "data" => { x: "y" } }.to_json)
|
||||||
expected_args = [NervesHub.device_path(ser),
|
expected_args = [NervesHub.device_path(ser),
|
||||||
{"tags":["foo"]}.to_json,
|
{ "tags": ["foo"] }.to_json,
|
||||||
NervesHub::HEADERS]
|
NervesHub::HEADERS]
|
||||||
|
|
||||||
expect(NervesHub.conn).to receive(:put).with(*expected_args).and_return(resp)
|
expect(NervesHub.conn).to receive(:put).with(*expected_args).and_return(resp)
|
||||||
|
@ -85,18 +84,18 @@ describe NervesHub do
|
||||||
it "calls `new_device` if device does not exist" do
|
it "calls `new_device` if device does not exist" do
|
||||||
expect(NervesHub.conn)
|
expect(NervesHub.conn)
|
||||||
xpect_args = "/orgs/farmbot/devices/X"
|
xpect_args = "/orgs/farmbot/devices/X"
|
||||||
resp = StubResp.new("404", { "data" => { } }.to_json)
|
resp = StubResp.new("404", { "data" => {} }.to_json)
|
||||||
expect(NervesHub.conn).to receive(:get).with(xpect_args).and_return(resp)
|
expect(NervesHub.conn).to receive(:get).with(xpect_args).and_return(resp)
|
||||||
tags = [ "A:B", "C:D" ]
|
tags = ["A:B", "C:D"]
|
||||||
xpect_args2 = [ "/orgs/farmbot/devices",
|
xpect_args2 = ["/orgs/farmbot/devices",
|
||||||
{ "description": "farmbot-X",
|
{ "description": "farmbot-X",
|
||||||
"identifier": "X",
|
"identifier": "X",
|
||||||
"tags": tags }.to_json,
|
"tags": tags }.to_json,
|
||||||
NervesHub::HEADERS ]
|
NervesHub::HEADERS]
|
||||||
data = {fake: "Farmbot"}
|
data = { fake: "Farmbot" }
|
||||||
resp2 = StubResp.new("201", { "data" => data }.to_json)
|
resp2 = StubResp.new("201", { "data" => data }.to_json)
|
||||||
expect(NervesHub.conn).to receive(:post).with(*xpect_args2).and_return(resp2)
|
expect(NervesHub.conn).to receive(:post).with(*xpect_args2).and_return(resp2)
|
||||||
expect(NervesHub.create_or_update("X", tags)).to eq(data)
|
expect(NervesHub.maybe_create_or_update("X", tags)).to eq(data)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sometimes performs the NERVES_HUB_CA_HACK" do
|
it "sometimes performs the NERVES_HUB_CA_HACK" do
|
||||||
|
@ -111,16 +110,16 @@ describe NervesHub do
|
||||||
it "detects malformed tags" do
|
it "detects malformed tags" do
|
||||||
tags = ["wrong", "also_wrong", "ok:tag"].shuffle
|
tags = ["wrong", "also_wrong", "ok:tag"].shuffle
|
||||||
serial_number = "0xCAFEF00D"
|
serial_number = "0xCAFEF00D"
|
||||||
expected = { error: NervesHub::BAD_TAG,
|
expected = { error: NervesHub::BAD_TAG,
|
||||||
serial_number: serial_number,
|
serial_number: serial_number,
|
||||||
tags: tags, }
|
tags: tags }
|
||||||
resp = StubResp.new("200", {
|
resp = StubResp.new("200", {
|
||||||
"data" => { hello: :world, identifier: "?" }
|
"data" => { hello: :world, identifier: "?" },
|
||||||
}.to_json)
|
}.to_json)
|
||||||
do_it = \
|
do_it =
|
||||||
receive(:get).with("/orgs/farmbot/devices/#{serial_number}").and_return(resp)
|
receive(:get).with("/orgs/farmbot/devices/#{serial_number}").and_return(resp)
|
||||||
expect(NervesHub.conn).to do_it
|
expect(NervesHub.conn).to do_it
|
||||||
expect(NervesHub).to receive(:report_problem).with(expected)
|
expect(NervesHub).to receive(:report_problem).with(expected)
|
||||||
NervesHub.create_or_update(serial_number, tags)
|
NervesHub.maybe_create_or_update(serial_number, tags)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue