Commit 5defef2e authored by Max Woolf's avatar Max Woolf
Browse files

test(rate_limit): replace FakeRedis with real Redis via docker compose

Drop the hand-rolled in-memory FakeRedis used in rate_limit_spec.rb in
favour of a real redis:7-alpine instance, so the rate-limit specs exercise
the same client semantics (TTL, pipelining, EXPIRE) as production.

Add docker-compose.yml exposing Redis on host port 6390 (offset to avoid
colliding with a local GDK / Caproni Redis on 6379).
spec/support/test_redis.rb auto-starts the container on first use and
registers an at_exit hook to tear it down again. Autostart is skipped in
CI (the rspec job now declares redis:7-alpine as a service) and when
LABKIT_TEST_REDIS_URL is explicitly set.

The "redis error" matrix case still uses an instance_double so it can
deterministically raise without depending on container state.

Co-Authored-By: default avatarClaude Opus 4.7 (1M context) <noreply@anthropic.com>
parent bbc780dd
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -30,6 +30,11 @@ include:
ruby-versions:
  extends: rspec
  image: ${CI_REGISTRY}/gitlab-com/gl-infra/common-ci-tasks-images/ruby:${RUBY_VERSION}
  services:
    - name: redis:7-alpine
      alias: redis
  variables:
    LABKIT_TEST_REDIS_URL: redis://redis:6379/0
  parallel:
    matrix:
      - RUBY_VERSION: ["3.4"]
@@ -38,6 +43,11 @@ ruby-versions:
legacy-ruby-versions:
  extends: rspec
  image: ruby:${RUBY_VERSION}
  services:
    - name: redis:7-alpine
      alias: redis
  variables:
    LABKIT_TEST_REDIS_URL: redis://redis:6379/0
  parallel:
    matrix:
      - RUBY_VERSION: ["3.3"]

.idea/workspace.xml

0 → 100644
+262 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ChangeListManager">
    <list default="true" id="eda9ef7d-e18e-4c0a-8966-bb3f714359b9" name="Changes" comment="">
      <change afterPath="$PROJECT_DIR$/docker-compose.yml" afterDir="false" />
      <change afterPath="$PROJECT_DIR$/spec/support/test_redis.rb" afterDir="false" />
      <change beforePath="$PROJECT_DIR$/.gitlab-ci.yml" beforeDir="false" afterPath="$PROJECT_DIR$/.gitlab-ci.yml" afterDir="false" />
      <change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
      <change beforePath="$PROJECT_DIR$/spec/labkit/rate_limit_spec.rb" beforeDir="false" afterPath="$PROJECT_DIR$/spec/labkit/rate_limit_spec.rb" afterDir="false" />
    </list>
    <option name="SHOW_DIALOG" value="false" />
    <option name="HIGHLIGHT_CONFLICTS" value="true" />
    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
    <option name="LAST_RESOLUTION" value="IGNORE" />
  </component>
  <component name="Git.Settings">
    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
  </component>
  <component name="GitRewordedCommitMessages">
    <option name="commitMessagesMapping">
      <RewordedCommitMessageMapping>
        <option name="originalMessage" value="feat(rate_limit): add Limiter#peek read-without-increment API&#10;&#10;Limiter#peek mirrors Limiter#check but does not mutate the Redis&#10;counter or extend its TTL. Implemented as a parallel rule-evaluation&#10;path on the Evaluator (pipelined GET + TTL replacing pipelined INCR +&#10;TTL + conditional EXPIRE).&#10;&#10;Result shape and matching semantics are identical to #check:&#10;&#10;- A missing Redis key reports count=0, matched=true, exceeded=false,&#10;  remaining=resolved_limit; reset_at falls back to the rule period&#10;  since there is no Redis-side window to read.&#10;- An existing key reports its current count; exceeded? compares&#10;  against the same resolved_limit used by #check, so peek and check&#10;  return consistent verdicts for the same observed state.&#10;- Non-matching rules and Redis errors fail open with the same Result&#10;  shape as #check (matched: false, error: true, action: :allow).&#10;&#10;Metrics:&#10;&#10;- New Metrics.peeks_total counter parallels calls_total. Separate&#10;  counter (rather than a label dimension on calls_total) keeps&#10;  existing dashboards intact and makes peek-vs-check usage observable&#10;  at a glance.&#10;- limit_gauge and period_gauge are set on matched peek so peek-only&#10;  call sites still observe their configured thresholds.&#10;- errors_total is shared with #check; a Redis failure is a Redis&#10;  failure regardless of which API was called.&#10;&#10;Motivates the GitLab cohort 3 migration of ApplicationRateLimiter&#10;peek callers to Labkit::RateLimit (work item&#10;https://gitlab.com/gitlab-com/gl-infra/production-engineering/-/work_items/28810)." />
        <option name="rewordedMessage" value="refactor(rate_limit): drop peek-specific counter and gauge updates&#10;&#10;Remove the peeks_total counter and the gauge/counter writes from the&#10;peek path. Peek is an observational read (frequently called as a&#10;&quot;have we already throttled?&quot; probe before the actual rate-limit&#10;decision); a parallel counter would either double-count peek+check&#10;pairs or invite operators to read peek volume as if it represented&#10;real rate-limit activity.&#10;&#10;The peek path now emits no per-call metric. errors_total is still&#10;shared between peek and check on the rationale that a Redis failure&#10;during peek is identical to a Redis failure during check from an&#10;operator's perspective. log_error stays as the WARN-level signal for&#10;peek-specific debugging.&#10;&#10;calls_total, limit_gauge, and period_gauge remain owned by check.&#10;Peek-only call sites will not surface gauge values, which is&#10;acceptable: the gauges are observational, not load-bearing for the&#10;rate-limit decision.&#10;&#10;&#10;feat(rate_limit): add Limiter#peek read-without-increment API&#10;&#10;Limiter#peek mirrors Limiter#check but does not mutate the Redis&#10;counter or extend its TTL. Implemented as a parallel rule-evaluation&#10;path on the Evaluator (pipelined GET + TTL replacing pipelined INCR +&#10;TTL + conditional EXPIRE).&#10;&#10;Result shape and matching semantics are identical to #check:&#10;&#10;- A missing Redis key reports count=0, matched=true, exceeded=false,&#10;  remaining=resolved_limit; reset_at falls back to the rule period&#10;  since there is no Redis-side window to read.&#10;- An existing key reports its current count; exceeded? compares&#10;  against the same resolved_limit used by #check, so peek and check&#10;  return consistent verdicts for the same observed state.&#10;- Non-matching rules and Redis errors fail open with the same Result&#10;  shape as #check (matched: false, error: true, action: :allow).&#10;&#10;Metrics:&#10;&#10;- New Metrics.peeks_total counter parallels calls_total. Separate&#10;  counter (rather than a label dimension on calls_total) keeps&#10;  existing dashboards intact and makes peek-vs-check usage observable&#10;  at a glance.&#10;- limit_gauge and period_gauge are set on matched peek so peek-only&#10;  call sites still observe their configured thresholds.&#10;- errors_total is shared with #check; a Redis failure is a Redis&#10;  failure regardless of which API was called.&#10;&#10;Motivates the GitLab cohort 3 migration of ApplicationRateLimiter&#10;peek callers to Labkit::RateLimit (work item&#10;https://gitlab.com/gitlab-com/gl-infra/production-engineering/-/work_items/28810)." />
      </RewordedCommitMessageMapping>
    </option>
    <option name="currentCommit" value="1" />
    <option name="onto" value="4674c6a9d878715a2b259fca79b34440a6d600ea" />
  </component>
  <component name="ProjectColorInfo">{
  &quot;associatedIndex&quot;: 1
}</component>
  <component name="ProjectId" id="3CoM5y3kKOUZWL7ruZQNxlJ1gQm" />
  <component name="ProjectViewState">
    <option name="hideEmptyMiddlePackages" value="true" />
    <option name="showLibraryContents" value="true" />
  </component>
  <component name="PropertiesComponent"><![CDATA[{
  "keyToString": {
    "ModuleVcsDetector.initialDetectionPerformed": "true",
    "RSpec.Labkit::RateLimit::Rule.executor": "Run",
    "RunOnceActivity.ShowReadmeOnStart": "true",
    "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
    "RunOnceActivity.git.unshallow": "true",
    "RunOnceActivity.typescript.service.memoryLimit.init": "true",
    "com.intellij.lang.ruby.rbs.tools.collection.workspace.sync.RbsCollectionUpdateProjectActivity#LAST_UPDATE_TIMESTAMP": "1777386083040",
    "git-widget-placeholder": "Rebasing testing/docker-compose-redis-autostart",
    "last_opened_file_path": "/Users/maxwoolf/labkit-ruby",
    "nodejs_package_manager_path": "npm",
    "ruby.structure.view.model.defaults.configured": "true",
    "settings.editor.selected.configurable": "preferences.pluginManager",
    "vue.rearranger.settings.migration": "true"
  }
}]]></component>
  <component name="RunManager">
    <configuration name="Labkit::RateLimit::Rule" type="RSpecRunConfigurationType" factoryName="RSpec" temporary="true" nameIsGenerated="true">
      <module name="labkit-ruby" />
      <predefined_log_file enabled="true" id="RUBY_RSPEC" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="RUBY_ARGS" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="WORK DIR" VALUE="$MODULE_DIR$" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="SHOULD_USE_SDK" VALUE="false" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="ALTERN_SDK_NAME" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="myPassParentEnvs" VALUE="true" />
      <envs>
        <env name="JRUBY_OPTS" value="-X+O" />
      </envs>
      <EXTENSION ID="BundlerRunConfigurationExtension" BUNDLE_MODE="AUTO" bundleExecEnabled="true" />
      <EXTENSION ID="RubyCoverageRunConfigurationExtension" runner="rcov">
        <COVERAGE_PATTERN ENABLED="true">
          <PATTERN REGEXPS="/.rvm/" INCLUDED="false" />
        </COVERAGE_PATTERN>
      </EXTENSION>
      <EXTENSION ID="org.jetbrains.plugins.ruby.rails.run.RailsRunConfigurationExtension" SCRATCH_USE_RAILS_RUNNER="false" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="TESTS_FOLDER_PATH" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="TEST_SCRIPT_PATH" VALUE="$MODULE_DIR$/spec/labkit/rate_limit/rule_spec.rb" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="TEST_SCRIPT_PATHS" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="SPEC_RUNNER_PATH" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="TEST_FILE_MASK" VALUE="**/*_spec.rb" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="SPEC_EXAMPLE_NAME" VALUE="Labkit::RateLimit::Rule" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="USE_EXAMPLE_MATCHES" VALUE="false" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="LINE_NUMBER_EXAMPLE_IDS" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="TEST_TEST_TYPE" VALUE="TEST_SCRIPT" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="SPEC_ARGS" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="RUNNER_VERSION" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="USE_CUSTOM_SPEC_RUNNER" VALUE="false" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="DRB" VALUE="false" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="ZEUS" VALUE="false" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="SPRING" VALUE="false" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="FULL_BACKTRACE" VALUE="false" />
      <method v="2" />
    </configuration>
    <configuration default="true" type="RSpecRunConfigurationType" factoryName="RSpec">
      <predefined_log_file enabled="true" id="RUBY_RSPEC" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="RUBY_ARGS" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="WORK DIR" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="SHOULD_USE_SDK" VALUE="false" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="ALTERN_SDK_NAME" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="myPassParentEnvs" VALUE="true" />
      <EXTENSION ID="BundlerRunConfigurationExtension" BUNDLE_MODE="AUTO" bundleExecEnabled="true" />
      <EXTENSION ID="RubyCoverageRunConfigurationExtension" runner="rcov">
        <COVERAGE_PATTERN ENABLED="true">
          <PATTERN REGEXPS="/.rvm/" INCLUDED="false" />
        </COVERAGE_PATTERN>
      </EXTENSION>
      <EXTENSION ID="org.jetbrains.plugins.ruby.rails.run.RailsRunConfigurationExtension" SCRATCH_USE_RAILS_RUNNER="false" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="TESTS_FOLDER_PATH" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="TEST_SCRIPT_PATH" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="TEST_SCRIPT_PATHS" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="SPEC_RUNNER_PATH" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="TEST_FILE_MASK" VALUE="**/*_spec.rb" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="SPEC_EXAMPLE_NAME" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="USE_EXAMPLE_MATCHES" VALUE="false" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="LINE_NUMBER_EXAMPLE_IDS" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="TEST_TEST_TYPE" VALUE="TEST_SCRIPT" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="SPEC_ARGS" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="RUNNER_VERSION" VALUE="" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="USE_CUSTOM_SPEC_RUNNER" VALUE="false" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="DRB" VALUE="false" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="ZEUS" VALUE="false" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="SPRING" VALUE="false" />
      <RSPEC_RUN_CONFIG_SETTINGS_ID NAME="FULL_BACKTRACE" VALUE="false" />
      <method v="2" />
    </configuration>
    <configuration default="true" type="RackRunConfigurationType" factoryName="Rack">
      <RACK_CONFIG_SETTINGS_ID NAME="RUBY_ARGS" VALUE="" />
      <RACK_CONFIG_SETTINGS_ID NAME="WORK DIR" VALUE="$PROJECT_DIR$" />
      <RACK_CONFIG_SETTINGS_ID NAME="SHOULD_USE_SDK" VALUE="false" />
      <RACK_CONFIG_SETTINGS_ID NAME="ALTERN_SDK_NAME" VALUE="" />
      <RACK_CONFIG_SETTINGS_ID NAME="myPassParentEnvs" VALUE="true" />
      <EXTENSION ID="BundlerRunConfigurationExtension" BUNDLE_MODE="AUTO" bundleExecEnabled="true" />
      <EXTENSION ID="RubyCoverageRunConfigurationExtension" runner="rcov">
        <COVERAGE_PATTERN ENABLED="true">
          <PATTERN REGEXPS="/.rvm/" INCLUDED="false" />
        </COVERAGE_PATTERN>
      </EXTENSION>
      <EXTENSION ID="org.jetbrains.plugins.ruby.rails.run.RailsRunConfigurationExtension" SCRATCH_USE_RAILS_RUNNER="false" />
      <RACK_CONFIG_SETTINGS_ID NAME="PORT" VALUE="9292" />
      <RACK_CONFIG_SETTINGS_ID NAME="IP" VALUE="127.0.0.1" />
      <RACK_CONFIG_SETTINGS_ID NAME="CONFIG_FILE" VALUE="" />
      <RACK_CONFIG_SETTINGS_ID NAME="RACK_SERVER_TYPE" VALUE="Default" />
      <RACK_CONFIG_SETTINGS_ID NAME="LAUNCH_JS" VALUE="false" />
      <method v="2" />
    </configuration>
    <configuration default="true" type="RakeRunConfigurationType" factoryName="Rake">
      <predefined_log_file enabled="true" id="RUBY_RAKE" />
      <RAKE_RUN_CONFIG_SETTINGS_ID NAME="RUBY_ARGS" VALUE="" />
      <RAKE_RUN_CONFIG_SETTINGS_ID NAME="WORK DIR" VALUE="" />
      <RAKE_RUN_CONFIG_SETTINGS_ID NAME="SHOULD_USE_SDK" VALUE="false" />
      <RAKE_RUN_CONFIG_SETTINGS_ID NAME="ALTERN_SDK_NAME" VALUE="" />
      <RAKE_RUN_CONFIG_SETTINGS_ID NAME="myPassParentEnvs" VALUE="true" />
      <EXTENSION ID="BundlerRunConfigurationExtension" BUNDLE_MODE="AUTO" bundleExecEnabled="true" />
      <EXTENSION ID="RubyCoverageRunConfigurationExtension" runner="rcov">
        <COVERAGE_PATTERN ENABLED="true">
          <PATTERN REGEXPS="/.rvm/" INCLUDED="false" />
        </COVERAGE_PATTERN>
      </EXTENSION>
      <EXTENSION ID="org.jetbrains.plugins.ruby.rails.run.RailsRunConfigurationExtension" SCRATCH_USE_RAILS_RUNNER="false" />
      <RAKE_RUN_CONFIG_SETTINGS_ID NAME="RAKE_TASK_NAME" VALUE="" />
      <RAKE_RUN_CONFIG_SETTINGS_ID NAME="RAKE_TASK_ARGS" VALUE="" />
      <RAKE_RUN_CONFIG_SETTINGS_ID NAME="RAKE_TASK_ATTACHED_TEST_FRAMEWORKS" VALUE="" />
      <RAKE_RUN_CONFIG_SETTINGS_ID NAME="RAKE_TASK_OPTION_TRACE" VALUE="false" />
      <RAKE_RUN_CONFIG_SETTINGS_ID NAME="RAKE_TASK_OPTION_DRYRUN" VALUE="false" />
      <RAKE_RUN_CONFIG_SETTINGS_ID NAME="RAKE_TASK_OPTION_PREREQS" VALUE="false" />
      <RAKE_RUN_CONFIG_SETTINGS_ID NAME="RAKE_TASK_OPTION_TESTOPTS" VALUE="" />
      <method v="2" />
    </configuration>
    <configuration default="true" type="RubyRunConfigurationType" factoryName="Ruby">
      <RUBY_RUN_CONFIG NAME="RUBY_ARGS" VALUE="" />
      <RUBY_RUN_CONFIG NAME="WORK DIR" VALUE="" />
      <RUBY_RUN_CONFIG NAME="SHOULD_USE_SDK" VALUE="false" />
      <RUBY_RUN_CONFIG NAME="ALTERN_SDK_NAME" VALUE="" />
      <RUBY_RUN_CONFIG NAME="myPassParentEnvs" VALUE="true" />
      <EXTENSION ID="BundlerRunConfigurationExtension" BUNDLE_MODE="AUTO" bundleExecEnabled="true" />
      <EXTENSION ID="RubyCoverageRunConfigurationExtension" runner="rcov">
        <COVERAGE_PATTERN ENABLED="true">
          <PATTERN REGEXPS="/.rvm/" INCLUDED="false" />
        </COVERAGE_PATTERN>
      </EXTENSION>
      <EXTENSION ID="org.jetbrains.plugins.ruby.rails.run.RailsRunConfigurationExtension" SCRATCH_USE_RAILS_RUNNER="false" />
      <RUBY_RUN_CONFIG NAME="SCRIPT_PATH" VALUE="" />
      <RUBY_RUN_CONFIG NAME="SCRIPT_ARGS" VALUE="" />
      <method v="2" />
    </configuration>
    <configuration default="true" type="TestUnitRunConfigurationType" factoryName="Test::Unit/Shoulda/Minitest">
      <predefined_log_file enabled="true" id="RUBY_TESTUNIT" />
      <RTEST_RUN_CONFIG_SETTINGS_ID NAME="RUBY_ARGS" VALUE="" />
      <RTEST_RUN_CONFIG_SETTINGS_ID NAME="WORK DIR" VALUE="" />
      <RTEST_RUN_CONFIG_SETTINGS_ID NAME="SHOULD_USE_SDK" VALUE="false" />
      <RTEST_RUN_CONFIG_SETTINGS_ID NAME="ALTERN_SDK_NAME" VALUE="" />
      <RTEST_RUN_CONFIG_SETTINGS_ID NAME="myPassParentEnvs" VALUE="true" />
      <EXTENSION ID="BundlerRunConfigurationExtension" BUNDLE_MODE="AUTO" bundleExecEnabled="true" />
      <EXTENSION ID="RubyCoverageRunConfigurationExtension" runner="rcov">
        <COVERAGE_PATTERN ENABLED="true">
          <PATTERN REGEXPS="/.rvm/" INCLUDED="false" />
        </COVERAGE_PATTERN>
      </EXTENSION>
      <EXTENSION ID="org.jetbrains.plugins.ruby.rails.run.RailsRunConfigurationExtension" SCRATCH_USE_RAILS_RUNNER="false" />
      <RTEST_RUN_CONFIG_SETTINGS_ID NAME="TESTS_FOLDER_PATH" VALUE="" />
      <RTEST_RUN_CONFIG_SETTINGS_ID NAME="TEST_SCRIPT_PATH" VALUE="" />
      <RTEST_RUN_CONFIG_SETTINGS_ID NAME="TEST_SCRIPTS_PATHS" VALUE="" />
      <RTEST_RUN_CONFIG_SETTINGS_ID NAME="TEST_FILE_MASK" VALUE="" />
      <RTEST_RUN_CONFIG_SETTINGS_ID NAME="TEST_METHOD_NAME" VALUE="" />
      <RTEST_RUN_CONFIG_SETTINGS_ID NAME="TEST_TEST_TYPE" VALUE="CUSTOM_SET_OF_FILES" />
      <RTEST_RUN_CONFIG_SETTINGS_ID NAME="DRB" VALUE="false" />
      <RTEST_RUN_CONFIG_SETTINGS_ID NAME="ZEUS" VALUE="false" />
      <RTEST_RUN_CONFIG_SETTINGS_ID NAME="SPRING" VALUE="false" />
      <RTEST_RUN_CONFIG_SETTINGS_ID NAME="RUNNER_OPTIONS" VALUE="" />
      <method v="2" />
    </configuration>
    <recent_temporary>
      <list>
        <item itemvalue="RSpec.Labkit::RateLimit::Rule" />
      </list>
    </recent_temporary>
  </component>
  <component name="SharedIndexes">
    <attachedChunks>
      <set>
        <option value="bundled-js-predefined-d6986cc7102b-9b0f141eb926-JavaScript-RM-253.29346.140" />
      </set>
    </attachedChunks>
  </component>
  <component name="SpringUtil" SPRING_PRE_LOADER_OPTION="true" RAKE_SPRING_PRE_LOADER_OPTION="true" RAILS_SPRING_PRE_LOADER_OPTION="true" />
  <component name="TaskManager">
    <task active="true" id="Default" summary="Default task">
      <changelist id="eda9ef7d-e18e-4c0a-8966-bb3f714359b9" name="Changes" comment="" />
      <created>1777047290773</created>
      <option name="number" value="Default" />
      <option name="presentableId" value="Default" />
      <updated>1777047290773</updated>
    </task>
    <servers />
  </component>
  <component name="TypeScriptGeneratedFilesManager">
    <option name="version" value="3" />
  </component>
  <component name="Vcs.Log.Tabs.Properties">
    <option name="TAB_STATES">
      <map>
        <entry key="MAIN">
          <value>
            <State>
              <option name="FILTERS">
                <map>
                  <entry key="branch">
                    <value>
                      <list>
                        <option value="rate-limit/limiter-peek" />
                      </list>
                    </value>
                  </entry>
                </map>
              </option>
            </State>
          </value>
        </entry>
      </map>
    </option>
  </component>
  <component name="com.intellij.coverage.CoverageDataManagerImpl">
    <SUITE FILE_PATH="coverage/labkit_ruby@Labkit__RateLimit__Rule.rcov" NAME="Labkit::RateLimit::Rule Coverage Results" MODIFIED="1777282843392" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="rcov" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" MODULE_NAME="labkit-ruby" />
  </component>
</project>
 No newline at end of file
+10 −0
Original line number Diff line number Diff line
@@ -43,6 +43,16 @@ $ # Run tests, linters
$ bundle exec rake verify
```

Some specs require a real Redis instance. When you run the suite locally,
it will automatically start one via `docker compose up -d redis` (see
`docker-compose.yml`) and tear it down again when the test process exits.
Redis is exposed on `localhost:6390` so it does not collide with a local
GDK/Caproni Redis on the default port.

To opt out of autostart (e.g. you've started Redis some other way), set
`LABKIT_TEST_REDIS_URL` to a reachable instance, or
`LABKIT_TEST_REDIS_NO_AUTOSTART=1` to fail loudly instead of spawning.

Please also review the [development section of the LabKit (go) README](https://gitlab.com/gitlab-org/labkit#developing-labkit) for details of the LabKit architectural philosophy.

To work on some of the scripts we use for releasing a new version,

docker-compose.yml

0 → 100644
+10 −0
Original line number Diff line number Diff line
services:
  redis:
    image: redis:7-alpine
    ports:
      - "6390:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 2s
      retries: 5
+42 −83

File changed.

Preview size limit exceeded, changes collapsed.

Loading