Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sm/test-ruby-language-bindings #1084

Closed
Closed
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
15 changes: 12 additions & 3 deletions .github/workflows/language-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
- name: Build schemas
run: npm run schemas

- name: zip languages dir
- name: Zip languages dir
run: zip -r languages.zip languages

- name: Upload schemas
Expand Down Expand Up @@ -80,7 +80,7 @@ jobs:
# - nodejs_wasm
# - php
- python
# - ruby
- ruby
# - swift

steps:
Expand All @@ -102,7 +102,10 @@ jobs:

- name: Create run-id
id: run-id
run: echo "RUN_ID=$(uuidgen)" >> $GITHUB_ENV
run: |
export RUN_ID=$(uuidgen)
echo "RUN_ID=$RUN_ID" >> $GITHUB_ENV
echo "RUN_ID=$RUN_ID"

- name: Get Setup Values
uses: bitwarden/sm-action@92d1d6a4f26a89a8191c83ab531a53544578f182 # v2.0.0
Expand Down Expand Up @@ -130,6 +133,12 @@ jobs:
working-directory: languages/language_tests
run: cargo run setup

- name: Install Ruby
if: matrix.language == 'ruby'
uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1.194.0
with:
ruby-version: '3.2' # Not needed with a .ruby-version file

- name: Run language tests
run: |
cd $GITHUB_WORKSPACE/languages/${{ matrix.language }}
Expand Down
1 change: 1 addition & 0 deletions languages/ruby/spec/.ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.2.5
9 changes: 9 additions & 0 deletions languages/ruby/spec/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

source 'https://rubygems.org'

gem 'bitwarden-sdk-secrets', path: '../bitwarden_sdk_secrets'

gem 'rspec', '~> 3.0'
gem 'rubocop', '~> 1.21'
gem 'pry', '~> 0.14'
35 changes: 35 additions & 0 deletions languages/ruby/spec/e2e_data_manipulation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
def env(name)
ENV[name] || raise("Missing ENV['#{name}']")
end

def with_run_id(str)
run_id = env('RUN_ID')
"#{str}-#{run_id}"
end

def filter_projects_to_this_run(projects)
run_id = env('RUN_ID')
projects.filter { |p| p['name'].end_with? run_id }
end

def filter_secrets_to_this_run(secrets)
run_id = env('RUN_ID')
secrets.filter { |p| p['key'].end_with? run_id }
end

def project_with_run_id(project)
project['name'] = with_run_id project['name']
project
end

def secret_with_run_id(secret)
secret['key'] = with_run_id secret['key']
secret['project_name'] = with_run_id secret['project_name']
secret
end

def secret_with_project_id(secret, projects)
project = projects.find { |p| p['name'] == secret['project_name'] }
secret['project_id'] = project['id']
secret
end
209 changes: 209 additions & 0 deletions languages/ruby/spec/e2e_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
require 'bitwarden-sdk-secrets'
require 'rspec/expectations'
require_relative 'e2e_data_manipulation'
require 'pry'

organization_id = env('ORGANIZATION_ID')
language_tests_path = File.join(File.dirname(__FILE__), '..', 'language_tests')
expected_data_file_name = 'e2e_data.json'
expected_data_file = env("TEST_DATA_FILE")

RSpec::Matchers.define :equal_secret do |expected|
match do |actual|
# Note: actual secrets are from the sdk, expected are parsed from the test data.
# actual SDK responses have the incorrect casing for organizationId and projectId.
actual['key'] == expected['key'] && actual['value'] == expected['value'] && actual['note'] == expected['note'] && actual['organizationId'] == organization_id && actual['projectId'] == expected['project_id']
end
end

RSpec::Matchers.define :equal_secret_identifier do |expected|
match do |actual|
actual['key'] == expected['key'] && actual['organizationId'] == organization_id
end
end

RSpec::Matchers.define :equal_project do |expected|
match do |actual|
actual['name'] == expected['name']
end
end

bitwarden_settings = BitwardenSDKSecrets::BitwardenSettings.new(env('API_URL'), env('IDENTITY_URL'))

describe 'Ruby Read E2E' do
let(:expected_data) { JSON.parse(File.read(expected_data_file)) }
let(:expected_projects) { expected_data['projects'].map { |p| project_with_run_id p } }
let(:expected_secrets) { expected_data['secrets'].map { |s| secret_with_project_id(secret_with_run_id(s), projects) } }
let(:all_projects) { @client.projects.list(organization_id).filter }
let(:projects) { filter_projects_to_this_run(all_projects) }
let(:all_secrets) { @client.secrets.list(organization_id) }
let(:secrets) { filter_secrets_to_this_run(all_secrets) }

before(:all) do
# Set up client
@state_file = File.join(language_tests_path, 'state.json')
@client = BitwardenSDKSecrets::BitwardenClient.new(bitwarden_settings)
@client.auth.login_access_token(env('ACCESS_TOKEN'), @state_file)
end

it 'should successfully list projects' do
expect(projects).to be_an_instance_of(Array)
end

it 'should successfully list secrets' do
expect(secrets).to be_an_instance_of(Array)
end

it 'should list the correct projects' do
expected_names = expected_projects.map { |p| p['name'] }
projects.each do |project|
expect(expected_names).to include(project['name'])
end
end

it 'should list the correct secrets' do
secrets.each do |secret|
expected_secret = expected_secrets.find { |s| s['key'] == secret['key'] }
expect(expected_secret).to_not be_nil
expect(secret).to equal_secret_identifier(expected_secret)
end
end

it 'should successfully get a project' do
project = projects.first
response = @client.projects.get(project['id'])
expect(response).to equal_project(project)
end

it 'should successfully get a secret' do
secret = secrets.first
response = @client.secrets.get(secret['id'])
expected_secret = expected_secrets.find { |s| s['key'] == secret['key'] }
expect(response).to equal_secret(expected_secret)
end

it 'should sync secrets' do
response = @client.secrets.sync(organization_id, nil)
expect(response).to be_an_instance_of(Hash)
expect(response['hasChanges']).to be_truthy
secrets = filter_secrets_to_this_run(response['secrets'])
secrets.each do |secret|
expected_secret = expected_secrets.find { |s| s['key'] == secret['key'] }
expect(secret).to equal_secret(expected_secret)
end
end

it 'should not have new sync changes' do
response = @client.secrets.sync(organization_id, Time.now.utc.strftime('%Y-%m-%dT%H:%M:%S.%6NZ'))
expect(response).to be_an_instance_of(Hash)
expect(response['hasChanges']).to be_falsey
expect(response['secrets']).to be_nil
end

after(:all) do
File.delete(@state_file) if File.exist?(@state_file)
end
end

describe 'Ruby Secrets Write E2E' do
let(:expected_data) { JSON.parse(File.read(expected_data_file)) }
let(:expected_projects) { expected_data['mutable_projects'].map { |p| project_with_run_id p } }
let(:expected_secrets) { expected_data['mutable_secrets'].map { |s| secret_with_project_id(secret_with_run_id(s), projects) } }
let(:projects) { @client.projects.list(organization_id) }
let(:write_project_name) { with_run_id('for_write_tests') }
let(:write_project) { projects.find { |p| p['name'] == write_project_name } }
let(:secrets) { @client.secrets.list(organization_id) }

before(:all) do
# Set up client
@state_file = File.join(language_tests_path, 'mutable_state.json')
@client = BitwardenSDKSecrets::BitwardenClient.new(bitwarden_settings)
@client.auth.login_access_token(env('MUTABLE_ACCESS_TOKEN'), @state_file)
end

it 'should successfully create a secret' do
secret = {
'key' => with_run_id('create_secret_key'),
'value' => 'create_secret_value',
'note' => 'create_secret_note',
'project_name' => write_project_name,
'project_id' => write_project['id']
}
created = @client.secrets.create(organization_id, secret['key'], secret['value'], secret['note'], [secret['project_id']])
expect(created).to equal_secret(secret)
end

it 'should delete a secret' do
to_delete = secrets.find { |s| s['key'] == with_run_id('to_delete') }
expect(to_delete).to_not be_nil
deleted = @client.secrets.delete([to_delete['id']])
expect(deleted).to be_an_instance_of(Array)
expect(deleted.length).to eq(1)
expect(deleted[0]['id']).to eq(to_delete['id'])
end

it 'should update a secret' do
to_update = secrets.find { |s| s['key'] == with_run_id('to_update') }
expect(to_update).to_not be_nil
updated_secret = {
'key' => with_run_id('updated_key'),
'value' => 'updated_value',
'note' => 'updated_note',
'project_id' => write_project['id']
}
updated = @client.secrets.update(organization_id, to_update['id'], updated_secret['key'], updated_secret['value'], updated_secret['note'], [write_project['id']])
expect(updated).to equal_secret(updated_secret)
end

after(:all) do
File.delete(@state_file) if File.exist?(@state_file)
end

end

describe 'Ruby Projects Write E2E' do
let(:expected_data) { JSON.parse(File.read(expected_data_file)) }
let(:expected_projects) { expected_data['mutable_projects'].map { |p| project_with_run_id p } }
let(:expected_secrets) { expected_data['mutable_secrets'].map { |s| secret_with_project_id(secret_with_run_id(s), projects) } }
let(:projects) { @client.projects.list(organization_id) }
let(:write_project_name) { with_run_id('for_write_tests') }
let(:write_project) { projects.find { |p| p['name'] == write_project_name } }

before(:all) do
# Set up client
@state_file = File.join(language_tests_path, 'mutable_state.json')
@client = BitwardenSDKSecrets::BitwardenClient.new(bitwarden_settings)
@client.auth.login_access_token(env('MUTABLE_ACCESS_TOKEN'), @state_file)
end

it 'should successfully create a project' do
to_create = {
'name' => with_run_id('created_project')
}
created = @client.projects.create(organization_id, to_create['name'])
expect(created).to equal_project(to_create)
end

it 'should successfully update a project' do
to_update = projects.find { |p| p['name'] == with_run_id('to_update') }
expect(to_update).to_not be_nil
updated_project = {
'name' => with_run_id('updated_project')
}
updated = @client.projects.update(organization_id, to_update['id'], updated_project['name'])
expect(updated).to equal_project(updated_project)
end

it 'should delete a project' do
to_delete = projects.find { |p| p['name'] == with_run_id('to_delete') }
expect(to_delete).to_not be_nil
deleted = @client.projects.delete([to_delete['id']])
expect(deleted).to be_an_instance_of(Array)
expect(deleted.length).to eq(1)
expect(deleted[0]['id']).to eq(to_delete['id'])
end

after(:all) do
File.delete(@state_file) if File.exist?(@state_file)
end
end
5 changes: 5 additions & 0 deletions languages/ruby/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh
cargo build --package bitwarden-c
cd spec
bundle install
bundle exec rspec e2e_spec.rb
Loading