Skip to content
Snippets Groups Projects
Commit 2193cfaf authored by Bruno Michel's avatar Bruno Michel
Browse files

Add a test for cleaning a deleted account

parent 3fbc78d8
No related branches found
No related tags found
No related merge requests found
Showing
with 296 additions and 17 deletions
...@@ -14,4 +14,4 @@ curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | ...@@ -14,4 +14,4 @@ curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh |
bin/golangci-lint run -E gofmt -E unconvert -E misspell -E whitespace --timeout 2m --max-same-issues 10 bin/golangci-lint run -E gofmt -E unconvert -E misspell -E whitespace --timeout 2m --max-same-issues 10
npm install eslint@5.16.0 prettier eslint-plugin-prettier eslint-config-cozy-app npm install eslint@5.16.0 prettier eslint-plugin-prettier eslint-config-cozy-app
./node_modules/.bin/eslint "assets/scripts/**" ./node_modules/.bin/eslint "assets/scripts/**" tests/integration/konnector/*.js
...@@ -10,3 +10,4 @@ gem "pry" ...@@ -10,3 +10,4 @@ gem "pry"
gem "pry-rescue" gem "pry-rescue"
gem "pry-stack_explorer" gem "pry-stack_explorer"
gem "rest-client" gem "rest-client"
gem "uuid"
...@@ -20,6 +20,8 @@ GEM ...@@ -20,6 +20,8 @@ GEM
i18n (1.0.0) i18n (1.0.0)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
interception (0.5) interception (0.5)
macaddr (1.7.2)
systemu (~> 2.6.5)
method_source (0.9.0) method_source (0.9.0)
mime-types (3.1) mime-types (3.1)
mime-types-data (~> 3.2015) mime-types-data (~> 3.2015)
...@@ -41,9 +43,12 @@ GEM ...@@ -41,9 +43,12 @@ GEM
http-cookie (>= 1.0.2, < 2.0) http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0) mime-types (>= 1.16, < 4.0)
netrc (~> 0.8) netrc (~> 0.8)
systemu (2.6.5)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.5) unf_ext (0.0.7.5)
uuid (2.3.9)
macaddr (~> 1.0)
websocket-driver (0.7.0) websocket-driver (0.7.0)
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.3) websocket-extensions (0.1.3)
...@@ -62,6 +67,7 @@ DEPENDENCIES ...@@ -62,6 +67,7 @@ DEPENDENCIES
pry-rescue pry-rescue
pry-stack_explorer pry-stack_explorer
rest-client rest-client
uuid
BUNDLED WITH BUNDLED WITH
1.16.4 1.16.4
...@@ -10,6 +10,7 @@ require 'open3' ...@@ -10,6 +10,7 @@ require 'open3'
require 'pbkdf2' require 'pbkdf2'
require 'pry' require 'pry'
require 'rest-client' require 'rest-client'
require 'uuid'
AwesomePrint.pry! AwesomePrint.pry!
Pry.config.history.file = File.expand_path "../tmp/.pry_history", __FILE__ Pry.config.history.file = File.expand_path "../tmp/.pry_history", __FILE__
......
let fs = require('fs')
let http = require('http')
let fields = JSON.parse(process.env['COZY_FIELDS'])
let credentials = process.env['COZY_CREDENTIALS']
let instance = process.env['COZY_URL']
let url = instance + 'data/io.cozy.accounts/' + fields.account
let options = {
headers: {
Authorization: `Bearer ${credentials}`
}
}
http.get(url, options, res => {
if (res.statusCode !== 200) {
throw new Error(`Status Code: ${res.statusCode}`)
}
res.setEncoding('utf8')
let rawData = ''
res.on('data', chunk => {
rawData += chunk
})
res.on('end', () => {
let data = JSON.parse(rawData)
fs.writeFileSync(data.log, JSON.stringify(data, null, ' '))
})
})
{
"aggregator": {
"accountId": "bank-aggregator"
},
"description": "A konnector to test accounts cleaning",
"developer": {
"name": "Cozy Cloud",
"url": "https://cozy.io/"
},
"editor": "Cozy Cloud",
"language": "node",
"license": "MIT",
"name": "bankkonn",
"on_delete_account": "on_delete.js",
"permissions": {
"accounts": {
"description": "Required to read the aggregator account",
"type": "io.cozy.accounts"
}
},
"slug": "bankkonn",
"type": "konnector",
"version": "0.1.0"
}
let fs = require('fs')
let http = require('http')
let fields = JSON.parse(process.env['COZY_FIELDS'])
let credentials = process.env['COZY_CREDENTIALS']
let instance = process.env['COZY_URL']
let url =
instance +
'data/io.cozy.accounts/' +
fields.account +
'?rev=' +
fields.account_rev
let options = {
headers: {
Authorization: `Bearer ${credentials}`
}
}
http.get(url, options, res => {
if (res.statusCode !== 200) {
throw new Error(`Status Code: ${res.statusCode}`)
}
res.setEncoding('utf8')
let rawData = ''
res.on('data', chunk => {
rawData += chunk
})
res.on('end', () => {
let data = JSON.parse(rawData)
url = instance + 'data/io.cozy.accounts/' + data.relationships.data._id
http.get(url, options, res2 => {
if (res2.statusCode !== 200) {
throw new Error(`Status Code: ${res2.statusCode}`)
}
res2.pipe(fs.createWriteStream(data.log))
})
})
})
class Account
include Model
attr_reader :name, :log
def self.doctype
"io.cozy.accounts"
end
def initialize(opts = {})
@couch_id = opts[:id]
@name = (opts[:name] || Faker::DrWho.character).gsub(/[^A-Za-z]/, '_')
@log = opts[:log] || "#{Helpers.current_dir}/account_#{@name}.log"
@aggregator = opts[:aggregator]
@type = opts[:type]
end
def as_json
json = {
name: @name,
log: @log,
account_type: @type
}.compact
if @aggregator
json[:relationships] = {
data: {
_id: @aggregator.couch_id,
_type: @aggregator.doctype
}
}
end
json
end
end
...@@ -56,11 +56,4 @@ class Album ...@@ -56,11 +56,4 @@ class Album
created_at: @created_at.rfc3339 created_at: @created_at.rfc3339
} }
end end
def as_reference
{
doctype: doctype,
id: @couch_id
}
end
end end
...@@ -62,11 +62,4 @@ class Contact ...@@ -62,11 +62,4 @@ class Contact
phone: @phones phone: @phones
} }
end end
def as_reference
{
doctype: doctype,
id: @couch_id
}
end
end end
...@@ -25,6 +25,18 @@ class Instance ...@@ -25,6 +25,18 @@ class Instance
@stack.install_app self, slug @stack.install_app self, slug
end end
def install_konnector(slug, source_url = nil)
@stack.install_konnector self, slug, source_url
end
def remove_konnector(slug)
@stack.remove_konnector self, slug
end
def run_konnector(slug, account_id)
@stack.run_konnector self, slug, account_id
end
def client def client
@client ||= RestClient::Resource.new url @client ||= RestClient::Resource.new url
end end
......
class Job
include Model
attr_reader :attributes
def self.doctype
"io.cozy.jobs"
end
def initialize(opts = {})
@couch_id = opts["id"]
@attributes = opts["attributes"]
end
def done?(inst)
status(inst) == "done"
end
def status(inst)
opts = {
accept: 'application/vnd.api+json',
authorization: "Bearer #{inst.token_for doctype}"
}
res = inst.client["/jobs/#{@couch_id}"].get opts
j = JSON.parse(res.body)
j.dig("data", "attributes", "state")
end
end
...@@ -7,10 +7,17 @@ module Model ...@@ -7,10 +7,17 @@ module Model
end end
end end
def to_json def to_json(*_args)
JSON.generate as_json JSON.generate as_json
end end
def as_reference
{
doctype: doctype,
id: @couch_id
}
end
def save(inst) def save(inst)
opts = { opts = {
content_type: :json, content_type: :json,
...@@ -27,6 +34,15 @@ module Model ...@@ -27,6 +34,15 @@ module Model
@couch_rev = j["rev"] @couch_rev = j["rev"]
end end
def delete(inst)
opts = {
accept: "application/vnd.api+json",
content_type: "application/vnd.api+json",
authorization: "Bearer #{inst.token_for doctype}"
}
inst.client["/data/#{doctype}/#{@couch_id}?rev=#{@couch_rev}"].delete opts
end
def doctype def doctype
self.class.doctype self.class.doctype
end end
......
...@@ -23,6 +23,10 @@ class Stack ...@@ -23,6 +23,10 @@ class Stack
@tokens = {} @tokens = {}
end end
def konnectors_cmd
File.expand_path "../../../scripts/konnector-node-run.sh", __dir__
end
def start def start
vault = File.join Helpers.current_dir, "vault" vault = File.join Helpers.current_dir, "vault"
FileUtils.mkdir_p vault FileUtils.mkdir_p vault
...@@ -32,7 +36,8 @@ class Stack ...@@ -32,7 +36,8 @@ class Stack
"--port", @port, "--admin-port", @admin, "--port", @port, "--admin-port", @admin,
"--fs-url", "file://#{Helpers.current_dir}/", "--fs-url", "file://#{Helpers.current_dir}/",
"--vault-encryptor-key", "#{vault}/key.enc", "--vault-encryptor-key", "#{vault}/key.enc",
"--vault-decryptor-key", "#{vault}/key.dec"] "--vault-decryptor-key", "#{vault}/key.dec",
"--konnectors-cmd", konnectors_cmd]
Helpers.spawn cmd.join(" "), log: "stack-#{@port}.log" Helpers.spawn cmd.join(" "), log: "stack-#{@port}.log"
sleep 1 sleep 1
end end
...@@ -68,6 +73,33 @@ class Stack ...@@ -68,6 +73,33 @@ class Stack
@apps[key] = system cmd.join(" ") @apps[key] = system cmd.join(" ")
end end
def install_konnector(inst, slug, source_url = nil)
cmd = ["cozy-stack", "konnectors", "install",
slug, source_url,
"--port", @port, "--admin-port", @admin,
"--domain", inst.domain, ">", "/dev/null"].compact
puts cmd.join(" ").green
system cmd.join(" ")
end
def remove_konnector(inst, slug)
cmd = ["cozy-stack", "konnectors", "uninstall", slug,
"--port", @port, "--admin-port", @admin,
"--domain", inst.domain, ">", "/dev/null"]
puts cmd.join(" ").green
system cmd.join(" ")
end
def run_konnector(inst, slug, account_id)
cmd = ["cozy-stack", "konnectors", "run", slug,
"--account-id", account_id,
"--port", @port, "--admin-port", @admin,
"--domain", inst.domain]
puts cmd.join(" ").green
out = `#{cmd.join(" ")}`.chomp
Job.new JSON.parse(out)
end
def token_for(inst, doctypes) def token_for(inst, doctypes)
key = inst.domain + "/" + doctypes.join(" ") key = inst.domain + "/" + doctypes.join(" ")
@tokens[key] ||= generate_token_for(inst, doctypes) @tokens[key] ||= generate_token_for(inst, doctypes)
......
class Trigger
include Model
def self.doctype
"io.cozy.triggers"
end
def initialize(opts = {})
@attributes = opts
end
def save(inst)
opts = {
content_type: :json,
accept: :json,
authorization: "Bearer #{inst.token_for doctype}"
}
res = inst.client["/jobs/triggers"].post to_json, opts
j = JSON.parse(res.body)
@couch_id = j["data"]["id"]
end
def as_json
{ data: { attributes: @attributes } }
end
end
require_relative '../boot'
require 'minitest/autorun'
require 'pry-rescue/minitest' unless ENV['CI']
def wait_for_file(file)
10.times do
sleep 1
return if File.exist? file
end
end
describe "An io.cozy.accounts" do
it "is cleaned via on_delete_account" do
Helpers.scenario "accounts_cleaning"
Helpers.start_mailhog
inst = Instance.create name: "Isabelle"
Account.create inst, name: "not a bank account"
source_url = "file://" + File.expand_path("../konnector", __dir__)
# 1. When an account is deleted, it is cleaned.
inst.install_konnector "bankone", source_url
aggregator = Account.create inst, id: ["bank-aggregator", UUID.generate].sample
accone = Account.create inst, type: "bankone", aggregator: aggregator,
name: Faker::HarryPotter.character
Trigger.create inst, worker: "konnector", type: "@cron", arguments: "@monthly",
message: { konnector: "bankone", account: accone.couch_id }
job = inst.run_konnector "bankone", accone.couch_id
done = false
10.times do
sleep 1
done = job.done?(inst)
break if done
end
assert done
executed = JSON.parse File.read(accone.log)
assert_equal executed["_id"], accone.couch_id
File.delete accone.log
accone.delete inst
wait_for_file accone.log
executed = JSON.parse File.read(accone.log)
assert_equal executed["_id"], aggregator.couch_id
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment