Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions spec/commands/auth/login_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
require "../../spec_helper"

describe TandaCLI::Commands::Auth::Login do
it "logs in and prompts for organisation selection when multiple organisations" do
scope = "me"

WebMock
.stub(:post, "https://eu.tanda.co/api/oauth/token")
.to_return(
status: 200,
body: {
access_token: "faketoken",
token_type: "test",
scope: scope,
created_at: TandaCLI::Utils::Time.now.to_unix,
}.to_json
)

WebMock
.stub(:get, endpoint("/users/me"))
.to_return(
status: 200,
body: {
name: "Test",
email: "test@example.com",
country: "United Kingdom",
time_zone: "Europe/London",
user_ids: [1, 2],
permissions: ["test"],
organisations: [
{
id: 1,
name: "Test Organisation 1",
locale: "en-GB",
country: "United Kingdom",
user_id: 1,
},
{
id: 2,
name: "Test Organisation 2",
locale: "en-GB",
country: "United Kingdom",
user_id: 2,
},
],
}.to_json
)

stdin = build_stdin(
"eu",
"test@example.com",
"dummypassword",
"2"
)

context = run(["auth", "login"], stdin: stdin)

expected = <<-OUTPUT
Site prefix (my, eu, us - Default is "my"):

What's your email?

What's your password?

Success: Retrieved token!
Which organisation would you like to use?
1: Test Organisation 1
2: Test Organisation 2

Enter a number:
Success: Selected organisation "Test Organisation 2"
Success: Organisations saved to config

OUTPUT

context.stdout.to_s.should eq(expected)
end

it "auto-selects organisation when only one is available" do
WebMock
.stub(:post, "https://eu.tanda.co/api/oauth/token")
.to_return(
status: 200,
body: {
access_token: "faketoken",
token_type: "test",
scope: "me",
created_at: TandaCLI::Utils::Time.now.to_unix,
}.to_json
)

WebMock
.stub(:get, endpoint("/users/me"))
.to_return(
status: 200,
body: {
name: "Test",
email: "test@example.com",
country: "United Kingdom",
time_zone: "Europe/London",
user_ids: [1],
permissions: ["test"],
organisations: [
{
id: 1,
name: "Test Organisation",
locale: "en-GB",
country: "United Kingdom",
user_id: 1,
},
],
}.to_json
)

stdin = build_stdin(
"eu",
"test@example.com",
"dummypassword",
)

context = run(["auth", "login"], stdin: stdin)

expected = <<-OUTPUT
Site prefix (my, eu, us - Default is "my"):

What's your email?

What's your password?

Success: Retrieved token!
Success: Selected organisation "Test Organisation"
Success: Organisations saved to config

OUTPUT

context.stdout.to_s.should eq(expected)
end

it "errors with invalid credentials" do
WebMock
.stub(:post, "https://eu.tanda.co/api/oauth/token")
.to_return(
status: 401,
body: {
error: "invalid_grant",
error_description: "The provided authorization grant is invalid",
}.to_json
)

stdin = build_stdin(
"eu",
"bad@example.com",
"wrongpassword",
)

context = run(["auth", "login"], stdin: stdin)

context.stderr.to_s.should contain("Unable to authenticate")
end
end
49 changes: 49 additions & 0 deletions spec/commands/auth/logout_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require "../../spec_helper"

describe TandaCLI::Commands::Auth::Logout do
it "revokes token and clears authentication for production environment" do
WebMock
.stub(:post, "https://eu.tanda.co/api/oauth/revoke")
.with(body: {token: "testtoken"}.to_json, headers: {"Content-Type" => "application/json"})
.to_return(status: 200, body: "{}")

context = run(["auth", "logout"])

context.stdout.to_s.should contain("Revoking access token...")
context.stdout.to_s.should contain("Revoked access token")
context.stdout.to_s.should contain("Logged out of production environment")
context.config.access_token.token.should be_nil
context.config.access_token.email.should be_nil
context.config.organisations.should be_empty
end

it "revokes token and clears authentication for staging environment" do
WebMock
.stub(:post, "https://staging.eu.tanda.co/api/oauth/revoke")
.with(body: {token: "testtoken"}.to_json, headers: {"Content-Type" => "application/json"})
.to_return(status: 200, body: "{}")

context = run(["auth", "logout"], config_fixture: :default_staging)

context.stdout.to_s.should contain("Revoking access token...")
context.stdout.to_s.should contain("Revoked access token")
context.stdout.to_s.should contain("Logged out of staging environment")
context.config.access_token.token.should be_nil
context.config.access_token.email.should be_nil
context.config.organisations.should be_empty
end

it "warns but still clears authentication when revocation fails" do
WebMock
.stub(:post, "https://eu.tanda.co/api/oauth/revoke")
.to_return(status: 503, body: "")

context = run(["auth", "logout"])

context.stdout.to_s.should contain("Revoking access token...")
context.stdout.to_s.should_not contain("Revoked access token")
context.stdout.to_s.should contain("Failed to revoke token (status: 503)")
context.stdout.to_s.should contain("Logged out of production environment")
context.config.access_token.token.should be_nil
end
end
28 changes: 28 additions & 0 deletions spec/commands/auth/status_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
require "../../spec_helper"

describe TandaCLI::Commands::Auth::Status do
it "displays authenticated status with details" do
context = run(["auth", "status"])

output = context.stdout.to_s
output.should contain("Authenticated (production)")
output.should contain("test@testmailfakenotrealthisisntarealdomainaaaa.com")
output.should contain("Test Organisation (user 1)")
output.should contain("eu")
end

it "displays authenticated status in staging" do
context = run(["auth", "status"], config_fixture: :default_staging)

output = context.stdout.to_s
output.should contain("Authenticated (staging)")
end

it "displays not authenticated when no token" do
context = run(["auth", "status"], config_fixture: :unauthenticated)

output = context.stdout.to_s
output.should contain("Not authenticated (production)")
output.should contain("Run `tanda_cli auth login` to authenticate")
end
end
78 changes: 0 additions & 78 deletions spec/commands/refetch_token_spec.cr

This file was deleted.

9 changes: 9 additions & 0 deletions spec/fixtures/configuration/unauthenticated.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"mode": "production",
"production": {
"site_prefix": "eu",
"access_token": {},
"organisations": []
},
"start_of_week": "saturday"
}
1 change: 1 addition & 0 deletions spec/support/configuration/fixture_file.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class Configuration::FixtureFile < TandaCLI::Configuration::AbstractFile
Default
DefaultStaging
NoRegularHours
Unauthenticated

def read : Bytes
file_name = to_s.underscore
Expand Down
35 changes: 9 additions & 26 deletions src/tanda_cli.cr
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,21 @@ module TandaCLI
display = Display.new(stdout, stderr)
input = Input.new(stdin, display)
config = Configuration.init(config_file, display)
current_user = user_from_config(config) || user_from_api(config, display, input)
client = build_client(config, display, input, current_user)
current = Current.new(current_user)
current_user = user_from_config(config)
client = build_client(config, current_user)
current = Current.new(current_user) if current_user

Context.new(
config,
client,
current,
display,
input
)
Context.new(config, client, current, display, input)
end

private def build_client(config : Configuration, display : Display, input : Input, current_user : Current::User? = nil) : API::Client
private def build_client(config : Configuration, current_user : Current::User? = nil) : API::Client?
token = config.access_token.token

# if a token can't be parsed from the config, get username and password from user and request a token
if token.nil?
API::Auth.fetch_new_token!(config, display, input)
return build_client(config, display, input)
end
return unless token

url = config.api_url
display.error!(url) unless url.is_a?(String)
API::Client.new(url, token, display, current_user)
return unless url.is_a?(String)

API::Client.new(url, token, current_user)
end

private def user_from_config(config : Configuration) : Current::User?
Expand All @@ -53,13 +43,6 @@ module TandaCLI

Current::User.new(organisation.user_id, organisation.name)
end

private def user_from_api(config : Configuration, display : Display, input : Input) : Current::User
client = build_client(config, display, input)
organisation = Request.ask_which_organisation_and_save!(client, config, display, input)

Current::User.new(organisation.user_id, organisation.name)
end
end

{% unless flag?(:test) %}
Expand Down
Loading