From a3c885463f9fa5709e351c427f582c52223e171b Mon Sep 17 00:00:00 2001 From: Max Wofford Date: Fri, 9 May 2025 11:55:39 -0400 Subject: [PATCH] Patch up user-agent matching and add simple tests for them --- .github/workflows/ci.yml | 45 +++++++++++++++++++ lib/wakatime_service.rb | 6 +-- test/lib/wakatime_service_test.rb | 73 +++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 test/lib/wakatime_service_test.rb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da63f79..d482385 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,6 +69,51 @@ jobs: - name: Run Rails Zeitwerk check run: bin/rails zeitwerk:check + test: + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:16-alpine + ports: + - 5432:5432 + # needed because the postgres container does not provide a healthcheck + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: test_db + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: .ruby-version + bundler-cache: true + + - name: Run tests + env: + RAILS_ENV: test + TEST_DATABASE_URL: postgres://postgres:postgres@localhost:5432/test_db + PGHOST: localhost + PGUSER: postgres + PGPASSWORD: postgres + run: | + bin/rails db:create RAILS_ENV=test + bin/rails db:migrate RAILS_ENV=test + bin/rails test + + - name: Keep screenshots from failed system tests + uses: actions/upload-artifact@v4 + if: failure() + with: + name: screenshots + path: ${{ github.workspace }}/tmp/screenshots + if-no-files-found: ignore + # test: # runs-on: ubuntu-latest diff --git a/lib/wakatime_service.rb b/lib/wakatime_service.rb index 4cfca39..bc69b69 100644 --- a/lib/wakatime_service.rb +++ b/lib/wakatime_service.rb @@ -75,18 +75,18 @@ class WakatimeService # Based on https://github.com/muety/wakapi/blob/b3668085c01dc0724d8330f4d51efd5b5aecaeb2/utils/http.go#L89 # Regex pattern to match wakatime client user agents - user_agent_pattern = /wakatime\/[^ ]+ \(([^)]+)\) [^ ]+ ([^\/]+)(?:\/([^\/]+))?/ + user_agent_pattern = /wakatime\/[^ ]+ \(([^)]+)\)(?: [^ ]+ ([^\/]+)(?:\/([^\/]+))?)?/ if matches = user_agent.match(user_agent_pattern) os = matches[1].split("-").first editor = matches[2] - editor ||= matches[3] + editor ||= "" { os: os, editor: editor, err: nil } else # Try parsing as browser user agent as fallback - if browser_ua = user_agent.match(/([^\/]+)\/([^\/]+)/) + if browser_ua = user_agent.match(/^([^\/]+)\/([^\/\s]+)/) { os: browser_ua[1], editor: browser_ua[2], err: nil } else { os: "", editor: "", err: "failed to parse user agent string" } diff --git a/test/lib/wakatime_service_test.rb b/test/lib/wakatime_service_test.rb new file mode 100644 index 0000000..727bea4 --- /dev/null +++ b/test/lib/wakatime_service_test.rb @@ -0,0 +1,73 @@ +require "test_helper" + +class WakatimeServiceTest < Minitest::Test + # Since parse_user_agent is a pure function that doesn't need database access, + # we can test it without loading any fixtures + def setup + ActiveRecord::FixtureSet.reset_cache + end + + def test_parse_user_agent_with_vscode_wakatime_client + user_agent = "wakatime/v1.0.0 (darwin-arm64) go1.0.0 vscode/1.0.0 vscode-wakatime/1.0.0" + result = WakatimeService.parse_user_agent(user_agent) + assert_equal "darwin", result[:os] + assert_equal "vscode", result[:editor] + assert_nil result[:error] + end + + def test_parse_user_agent_with_GitHub_Desktop + user_agent = "wakatime/v1.0.0 (darwin-arm64) go1.0.0 github-desktop/1.0.0" + result = WakatimeService.parse_user_agent(user_agent) + assert_equal "darwin", result[:os] + assert_equal "github-desktop", result[:editor] + assert_nil result[:error] + end + + def test_parse_user_agent_with_Figma + user_agent = "wakatime/v1.0.0 (darwin-arm64) go1.0.0 figma/1.0.0" + result = WakatimeService.parse_user_agent(user_agent) + assert_equal "darwin", result[:os] + assert_equal "figma", result[:editor] + assert_nil result[:error] + end + + def test_parse_user_agent_with_Terminal + user_agent = "wakatime/v1.0.0 (darwin-arm64) go1.0.0 terminal/1.0.0" + result = WakatimeService.parse_user_agent(user_agent) + assert_equal "darwin", result[:os] + assert_equal "terminal", result[:editor] + assert_nil result[:error] + end + + def test_parse_user_agent_with_vim + user_agent = "wakatime/v1.0.0 (darwin-arm64) go1.0.0 vim/1.0.0" + result = WakatimeService.parse_user_agent(user_agent) + assert_equal "darwin", result[:os] + assert_equal "vim", result[:editor] + assert_nil result[:error] + end + + def test_parse_user_agent_with_Windows + user_agent = "wakatime/v1.0.0 (windows-x86_64) go1.0.0 vscode/1.0.0" + result = WakatimeService.parse_user_agent(user_agent) + assert_equal "windows", result[:os] + assert_equal "vscode", result[:editor] + assert_nil result[:error] + end + + def test_parse_user_agent_with_Cursor + user_agent = "wakatime/v1.0.0 (darwin-arm64) go1.0.0 cursor/1.0.0" + result = WakatimeService.parse_user_agent(user_agent) + assert_equal "darwin", result[:os] + assert_equal "cursor", result[:editor] + assert_nil result[:error] + end + + def test_parse_user_agent_with_invalid_user_agent + user_agent = "invalid-user-agent" + result = WakatimeService.parse_user_agent(user_agent) + assert_equal "", result[:os] + assert_equal "", result[:editor] + assert_equal "failed to parse user agent string", result[:err] + end +end \ No newline at end of file