{"id":16443394,"url":"https://github.com/0exp/sidekiq_portal","last_synced_at":"2025-03-23T08:32:05.822Z","repository":{"id":45389977,"uuid":"229419622","full_name":"0exp/sidekiq_portal","owner":"0exp","description":"🕒 Scheduled jobs invocation emulation for test environments (eliminate time traveling by might and magic 😈)","archived":false,"fork":false,"pushed_at":"2024-11-13T22:46:27.000Z","size":6382,"stargazers_count":9,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-18T19:28:39.255Z","etag":null,"topics":["sidekiq-cron","sidekiq-portal","sidekiq-rspec","sidekiq-scheduler","sidekiq-test","sidekiq-timecop"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/0exp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-12-21T11:44:35.000Z","updated_at":"2024-11-13T22:46:31.000Z","dependencies_parsed_at":"2024-10-28T15:33:42.045Z","dependency_job_id":"9c0cf363-2e60-4934-a6f2-48097b2c003a","html_url":"https://github.com/0exp/sidekiq_portal","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0exp%2Fsidekiq_portal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0exp%2Fsidekiq_portal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0exp%2Fsidekiq_portal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0exp%2Fsidekiq_portal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0exp","download_url":"https://codeload.github.com/0exp/sidekiq_portal/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245078067,"owners_count":20557274,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["sidekiq-cron","sidekiq-portal","sidekiq-rspec","sidekiq-scheduler","sidekiq-test","sidekiq-timecop"],"created_at":"2024-10-11T09:20:20.410Z","updated_at":"2025-03-23T08:32:05.803Z","avatar_url":"https://github.com/0exp.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sidekiq::Portal [![Gem Version](https://badge.fury.io/rb/sidekiq_portal.svg)](https://badge.fury.io/rb/sidekiq_portal)\n\n\u003e hackaton slides: [link](https://github.com/0exp/sidekiq_portal/blob/master/docs/umbrellio_hackaton_v1.0.pdf)\n\n\u003e meetup slides: [link](https://github.com/0exp/sidekiq_portal/blob/master/docs/sidekiq_portal_ruby_group_meetup.pdf)\n\n\u003e meetup video: [link](https://youtu.be/H3SafkpBQ_w?t=12288)\n\n**Sidekiq::Portal** - scheduled jobs runner for your test environments,\nwhich execution process must occur during the `Timecop.travel(...)` operations according to the scheduler config.\n\nEach job starts at the time it was supposed to start according to the scheduler plan -\nthe internal `Time.current` expression will give you exactly the scheduler-planned time.\n\nSupports **ActiveJob** backend (**Sidekiq::Worker** coming soon). Works with **sidekiq-scheduler**-based job configs (**sidekiq-cron** coming soon).\n\nRealized as an instance, but have a global-based implementation too.\n\n## Table of contents\n\n- [Installation](#installation)\n- [Configuration](#configuration)\n- [Example](#example)\n- [Roadmap](#roadmap)\n- [License](#license)\n- [Authors](#authors)\n\n## Installation\n\n- in your `Gemfile`\n\n```ruby\ngem 'sidekiq', '\u003e= 5' # runtime dependency\ngem 'timecop', '~\u003e 0.9' # runtime dependency\n\ngroup :test do\n  gem 'rspec'\n  gem 'sidekiq_portal'\nend\n```\n\n- run shell command:\n\n```shell\nbundle install\n```\n\n- `spec_helper.rb`:\n\n```ruby\nrequire 'timecop' # runtime dependency\nrequire 'sidekiq' # runtime dependency\nrequire 'sidekiq/api'\nrequire 'sidekiq/testing'\n\nrequire 'sidekiq_portal'\n```\n\n## Configuration\n\n- `default_timezone` - global time zone for your jobs (`UTC` by default);\n- `retry_count` - Sidekiq's built-in retry mechanism simulation (`0` by default);\n- `retry_on` - retry only on a set of exceptions (`[StandardError]` by default);\n- `scheduler_config` - `sidekiq-scheduler`-based scheduler configuration (`{}` by default (non-configured));\n- `Sidekiq::Portal.reload!(\u0026configuration)` - reload portal configurations;\n\nIn your `spec_helper.rb`:\n\n```ruby\n# portal configuration\nSidekiq::Portal.setup! do |config|\n  config.default_timezone = 'UTC' # 'UTC' by default\n  config.retry_count = 3 # 0 by default\n  config.retry_on = [StandardError] # [StandardError] by default\n\n  # pre-defined sidekiq-scheduler configs (Rails example)\n  config.scheduler_config = Rails.application.config_for(:sidekiq)[:schedule]\n\n  # manual sidekiq-scheduler configs\n  config.scheduler_config = {\n    LoolJob: { every: '15m' },\n    kek_job: { cron: '0 * * * * *', class: :KekJob }\n  }\nend\n\n# global state clearing logic\nRSpec.configure do |config|\n  config.before { Sidekiq::Worker.clear_all }\n  config.after  { Timecop.return }\n  config.after  { Sidekiq::Portal.reload! }\nend\n```\n\nAnd in your tests:\n\n```ruby\nRSpec.describe 'Some spec' do\n  specify 'magic?' do\n    Timecop.travel(Time.current + 2.hours) # magic begins here 😈\n  end\nend\n```\n\n## Example\n\n- Job class:\n\n```ruby\nclass HookExampleJob \u003c ApplicationJob\n  def perform\n    GLOBAL_HOOK_INTERCEPTOR \u003c\u003c Time.current # intercept current time\n  end\nend\n```\n\n- `Sidekiq::Scheduler` config:\n\n```yaml\n:schedule:\n  HookExample:\n    every: '15m'\n```\n\n- `HookExampleJob` spec:\n\n```ruby\nRSpec.describe 'HookExampleJob sheduler plan' do\n  specify 'scheduled?' do\n    stub_const('GLOBAL_HOOK_INTERCEPTOR', [])\n    expect(GLOBAL_HOOK_INTERCEPTOR.count).to eq(0) # =\u003e true\n\n    # do some magic 😈\n    Timecop.travel(Time.current + 2.hours)\n\n    expect(GLOBAL_HOOK_INTERCEPTOR.count).to eq(8) # =\u003e true (😈 magic)\n\n    puts GLOBAL_HOOK_INTERCEPTOR # 😈\n    # =\u003e outputs:\n    # 2019-12-24 03:05:39 +0300 (+15m) (Time.current from HookExampleJob#perform)\n    # 2019-12-24 03:20:39 +0300 (+15m)\n    # 2019-12-24 03:35:39 +0300 (+15m)\n    # 2019-12-24 03:50:39 +0300 (+15m)\n    # 2019-12-24 04:05:39 +0300 (+15m)\n    # 2019-12-24 04:20:39 +0300 (+15m)\n    # 2019-12-24 04:35:39 +0300 (+15m)\n    # 2019-12-24 04:50:39 +0300 (+15m)\n  end\nend\n```\n\n## Roadmap\n\n- `Sidekiq::Testing.portal!` test mode with support for `:inline` and `:fake`;\n  (`Sidekiq::Testing.inline!` and `Sidekiq::Testing.fake` respectively);\n- support for `ActiveSupport::TimeZone` instances in `default_timezone` config;\n- rspec matchers;\n- `#reload!` should use previously defined settings?;\n- support for `Sidekiq::Worker` job backend;\n- support for `Sidekiq::Cron` scheduler plans;\n- more specs;\n- documentation and examples for instance-based portals (`Sidekiq::Portal.new(\u0026configuration)`);\n- configurable job execution randomization (for jobs which should be invoked at the same time)\n  (randomized invocation and not - at the same time or not);\n- configurable in-line invocations (with job list config);\n- configurable and conditional portal invocation (run over all specs or only over the one or etc)\n  (suitable for unit tests);\n- support for **Ruby 2.7**;\n- **Time** as external dependency;\n- getting rid of **ActiveSupport**'s **Time**-related core extensions;\n- better specs;\n\n## License\n\nReleased under MIT License.\n\n## Authors\n\n[Rustam Ibragimov](https://github.com/0exp)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0exp%2Fsidekiq_portal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0exp%2Fsidekiq_portal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0exp%2Fsidekiq_portal/lists"}