Skip to content
Open
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
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
VOUCHERIFY_HOST=https://api.voucherify.io
X_APP_ID=
X_APP_TOKEN=
X_MANAGEMENT_ID=
X_MANAGEMENT_TOKEN=
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ jobs:
-e VOUCHERIFY_HOST=${{ vars.VOUCHERIFY_HOST }} \
-e X_APP_ID=${{ secrets.X_APP_ID }} \
-e X_APP_TOKEN=${{ secrets.X_APP_TOKEN }} \
-e X_MANAGEMENT_ID=${{ secrets.X_MANAGEMENT_ID }} \
-e X_MANAGEMENT_TOKEN=${{ secrets.X_MANAGEMENT_TOKEN }} \
ruby
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ WORKDIR /app
COPY . .

RUN gem build VoucherifySdk.gemspec
RUN gem install voucherify-8.0.3.gem
RUN gem install voucherify-8.0.4.gem
RUN gem install dotenv
RUN gem install rspec

Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,17 @@ Once set up, check the following methods to give Voucherify a more interesting s

## 🐳 Run local tests with docker

1. Copy `.env.example` to `.env` and fill in the values.
1. Copy `.env.example` to `.env` and fill in:
- `VOUCHERIFY_HOST`
- `X_APP_ID`
- `X_APP_TOKEN`
- `X_MANAGEMENT_ID`
- `X_MANAGEMENT_TOKEN`
2. Run `docker build -t ruby .` to build the image.
3. Run `docker run --rm ruby` to run the tests and delete container immediately after.

You can also run tests from monorepo root with `npm run test-ruby-sdk`.

## 🛠️ Contribute

Do you want to contribute?
Expand Down Expand Up @@ -201,6 +208,13 @@ end

## 📅 Changelog

- **2026-05-19** - `8.0.4`
- Added test coverage for campaign CSV voucher import.
- Added test coverage for campaign `expiration_date` update to `null`.
- Added management test coverage: list projects, list metadata schemas, update metadata schema.
- Added a dedicated model test for objects combining regular fields and dynamic properties.
- Updated test setup docs and `sdks/ruby/.env.example` with management credentials.
- Minor cleanups: fixed typos.
- **2024-01-29** - `8.0.3`
- Added support for **GET** /v1/loyalties/{campaignId}/pending-points
- Added support for **GET** /v1/loyalties/members/{memberId}/pending-points
Expand Down
18 changes: 18 additions & 0 deletions __tests__/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ The primary reason for this approach is to ensure that the most critical aspects
We strongly recommend running the tests using the Dockerfile provided in the root directory.
If you prefer to run tests locally, here are the steps you need to follow.

### Environment variables

Create `sdks/ruby/.env` (you can copy `sdks/ruby/.env.example`) and set:

- `VOUCHERIFY_HOST`
- `X_APP_ID`
- `X_APP_TOKEN`
- `X_MANAGEMENT_ID`
- `X_MANAGEMENT_TOKEN`

### Requirements

- all elements described in [main README.md](../README.md) like Ruby and Gem manager
Expand All @@ -23,6 +33,14 @@ If you prefer to run tests locally, here are the steps you need to follow.
4. Go into `__tests__` directory
5. Run `rspec ./spec` command

### Running with Docker (recommended)

1. Go into `sdks/ruby` directory
2. Build image: `docker build -t ruby .`
3. Run tests: `docker run --rm ruby`

From repo root, you can also run: `npm run test-ruby-sdk`.

**Note that running tests will affect your Voucherify project data. Run tests only on development projects.**

## Contributing
Expand Down
54 changes: 53 additions & 1 deletion __tests__/lib/campaigns.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require_relative 'utils.rb'
require 'VoucherifySdk'
require 'json'
require 'tempfile'

def create_validation_rule_applicable_to(validation_rules_api_instance, product_id)
begin
Expand Down Expand Up @@ -144,5 +145,56 @@ def add_vouchers_to_campaign(campaigns_api_instance, campaign_id, voucher_count)
rescue VoucherifySdk::ApiError => e
return nil
end
end
end

def create_discount_campaign_with_expiration(campaigns_api_instance, expiration_date)
expiration_value = expiration_date.respond_to?(:utc) ? expiration_date.utc.iso8601 : expiration_date

campaigns_api_instance.create_campaign({
campaigns_create_request_body: VoucherifySdk::CampaignsCreateRequestBody.new({
campaign_type: "DISCOUNT_COUPONS",
name: generate_random_string(),
type: "AUTO_UPDATE",
expiration_date: expiration_value,
voucher: VoucherifySdk::CampaignsCreateRequestBodyVoucher.new({
type: 'DISCOUNT_VOUCHER',
discount: VoucherifySdk::Discount.new({
type: 'AMOUNT',
amount_off: 1000
})
})
})
})
end

def update_campaign_expiration_to_null(campaigns_api_instance, campaign_id)
campaigns_api_instance.update_campaign(campaign_id, {
campaigns_update_request_body: {
expiration_date: nil
}
})
true
end

def import_vouchers_to_campaign_using_csv(campaigns_api_instance, campaign_id, voucher_codes)
file = Tempfile.new(['vouchers_import', '.csv'])

begin
file.write("code\n")
voucher_codes.each do |code|
file.write("#{code}\n")
end
file.rewind

csv_file = File.new(file.path)
result = campaigns_api_instance.import_vouchers_to_campaign_using_csv(campaign_id, {
file: csv_file
})
csv_file.close
result
ensure
file.close
file.unlink
end
end

21 changes: 21 additions & 0 deletions __tests__/lib/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ def self.events_api_instance
return VoucherifySdk::EventsApi.new()
end

def self.async_actions_api_instance
return VoucherifySdk::AsyncActionsApi.new()
end

def self.order_api_instance
return VoucherifySdk::OrdersApi.new()
end
Expand All @@ -71,5 +75,22 @@ def self.loyalties_api_instance
def self.vouchers_api_instance
return VoucherifySdk::VouchersApi.new()
end

def self.management_api_instance
management_config = VoucherifySdk::Configuration.new
management_config.api_key['X-Management-Id'] = ENV['X_MANAGEMENT_ID']
management_config.api_key['X-Management-Token'] = ENV['X_MANAGEMENT_TOKEN']
management_config.host = ENV['VOUCHERIFY_HOST'] || 'https://api.voucherify.io'

return VoucherifySdk::ManagementApi.new(VoucherifySdk::ApiClient.new(management_config))
end

def self.management_credentials_configured?
return !ENV['X_MANAGEMENT_ID'].to_s.empty? && !ENV['X_MANAGEMENT_TOKEN'].to_s.empty?
end

def self.management_project_id
return ENV['PROJECT_ID']
end
end

2 changes: 1 addition & 1 deletion __tests__/lib/loyalties.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ def list_loyalty_card_transactions(loyalties_api_instance, memberId)

rescue VoucherifySdk::ApiError => e
return nil
end
end
end
28 changes: 27 additions & 1 deletion __tests__/lib/utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,30 @@ def generate_random_string(length=10)

def generate_random_voucher_codes(base_code, count, length=6)
(1..count).map { "#{base_code}-#{generate_random_string(length)}" }
end
end

# Polls the async actions endpoint until the action reaches a terminal state.
# Returns the final AsyncActionGetResponseBody when it succeeds, raises otherwise.
def wait_for_async_action(async_actions_api_instance, async_action_id, max_attempts: 10, delay_seconds: 2)
attempt = 0

loop do
attempt += 1
sleep(delay_seconds)

action = async_actions_api_instance.get_async_action(async_action_id)

operation_status = action.operation_status.to_s.upcase
status = action.status.to_s.upcase

return action if operation_status == 'SUCCESS' || status == 'DONE'

if operation_status == 'FAILED' || status == 'FAILED'
raise "Async action #{async_action_id} failed (status=#{status}, operation_status=#{operation_status})"
end

if attempt >= max_attempts
raise "Async action #{async_action_id} did not finish after #{max_attempts} attempts (status=#{status}, operation_status=#{operation_status})"
end
end
end
35 changes: 33 additions & 2 deletions __tests__/spec/02_campaigns_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
before(:each) do
@campaigns_api_instance = Config.campaigns_api_instance()
@validation_rules_api_instance = Config.validation_rules_api_instance()
@async_actions_api_instance = Config.async_actions_api_instance()
@voucherify_data = VoucherifyData.instance()
end

Expand Down Expand Up @@ -44,7 +45,7 @@
@voucherify_data.set_discount_campaign(created_discount_campaign)
end

it 'create a promotion campaign', :order => :thrid do
it 'create a promotion campaign', :order => :third do
created_promotion_campaign = create_promotion_campaign(@campaigns_api_instance)

snapshot_name = 'campaigns/created_promotion_campaign'
Expand Down Expand Up @@ -125,9 +126,39 @@
expect(validate_deep_match(filtered_validation_rule_snapshot, validation_rule)).to be true

voucher = @campaigns_api_instance.add_vouchers_to_campaign(campaign.id, {
vouchers_count: 1,
vouchers_count: 1,
})

@voucherify_data.set_voucher_with_more_than_validation_rule(voucher)
end

it 'import vouchers to campaign using csv', :order => :tenth do
voucher_codes = Array.new(3) { generate_random_string(8) }
imported_vouchers = import_vouchers_to_campaign_using_csv(@campaigns_api_instance, $created_discount_campaign.id, voucher_codes)

expect(imported_vouchers).not_to be_nil
expect(imported_vouchers.async_action_id).not_to be_nil

finished_action = wait_for_async_action(@async_actions_api_instance, imported_vouchers.async_action_id)

expect(finished_action).not_to be_nil
expect(finished_action.id).to eq(imported_vouchers.async_action_id)
end

it 'update campaign expiration_date to null', :order => :eleventh do
expiration_date = Time.now.utc + 86400
created_campaign = create_discount_campaign_with_expiration(@campaigns_api_instance, expiration_date)

expect(created_campaign).not_to be_nil
expect(created_campaign.expiration_date).not_to be_nil

updated_to_null = update_campaign_expiration_to_null(@campaigns_api_instance, created_campaign.id)

expect(updated_to_null).to be true

campaign_after_update = @campaigns_api_instance.get_campaign(created_campaign.id)

expect(campaign_after_update).not_to be_nil
expect(campaign_after_update.expiration_date).to be_nil
end
end
19 changes: 16 additions & 3 deletions __tests__/spec/04_publications_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
customer: VoucherifySdk::Customer.new({
id: @voucherify_data.get_customer().id
}),
campaign: VoucherifySdk::CreatePublicationCampaign.new({
name: @voucherify_data.get_discount_campaign().name
})
voucher: @voucherify_data.get_voucher().code
})
})

Expand All @@ -28,4 +26,19 @@
expect(publication).not_to be_nil
expect(validate_deep_match(filtered_snapshot, publication)).to be true
end

it 'publish loyalty card to customer', :order => :second do
publication = @publications_api_instance.create_publication({
join_once: true,
publications_create_request_body: VoucherifySdk::PublicationsCreateRequestBody.new({
customer: VoucherifySdk::Customer.new({
id: @voucherify_data.get_customer().id
}),
voucher: @voucherify_data.get_loyalty_card().code
})
})

expect(publication).not_to be_nil
expect(publication.voucher.code).to eq(@voucherify_data.get_loyalty_card().code)
end
end
2 changes: 1 addition & 1 deletion __tests__/spec/07_vouchers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
expect(validate_deep_match(filtered_snapshot, voucher)).to be true
end

it 'enable voucher previously created', :order => :thrird do
it 'enable voucher previously created', :order => :third do
voucher = @vouchers_api_instance.enable_voucher(@voucherify_data.get_voucher().code)

snapshot_name = 'vouchers/enabled_previously_created_voucher'
Expand Down
Loading