Back to Repositories

Testing LastPass Secret Management Integration in Kamal

This test suite validates the LastPass adapter functionality in Kamal, ensuring secure secret management and retrieval through the LastPass CLI. The tests verify authentication, secret fetching, and folder-based operations.

Test Coverage Overview

The test suite provides comprehensive coverage of LastPass integration functionality:

  • Basic secret fetching from LastPass vault
  • Folder-based secret retrieval
  • Authentication and login verification
  • CLI installation validation
  • Error handling for missing dependencies

Implementation Analysis

The testing approach utilizes Minitest’s assertion framework with stub_ticks for command-line interaction simulation. The implementation follows a structured pattern of mocking LastPass CLI commands and validating JSON responses, ensuring reliable secret management integration.

Key patterns include command stubbing, JSON response validation, and authentication state verification.

Technical Details

Testing tools and configuration:

  • Minitest framework for test organization
  • stub_ticks for CLI command mocking
  • JSON parsing and validation
  • Shell command escape handling
  • Fixture-based configuration

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Isolated test cases with proper setup
  • Comprehensive error scenario coverage
  • Clean command-line interaction mocking
  • Structured JSON response validation
  • Clear test case organization and naming

basecamp/kamal

test/secrets/last_pass_adapter_test.rb

            
require "test_helper"

class LastPassAdapterTest < SecretAdapterTestCase
  setup do
    `true` # Ensure $? is 0
  end

  test "fetch" do
    stub_ticks.with("lpass --version 2> /dev/null")
    stub_ticks.with("lpass status --color never").returns("Logged in as [email protected].")

    stub_ticks
      .with("lpass show SECRET1 FOLDER1/FSECRET1 FOLDER1/FSECRET2 --json")
      .returns(<<~JSON)
        [
          {
            "id": "1234567891234567891",
            "name": "SECRET1",
            "fullname": "SECRET1",
            "username": "",
            "password": "secret1",
            "last_modified_gmt": "1724926054",
            "last_touch": "1724926639",
            "group": "",
            "url": "",
            "note": ""
          },
          {
            "id": "1234567891234567892",
            "name": "FSECRET1",
            "fullname": "FOLDER1/FSECRET1",
            "username": "",
            "password": "fsecret1",
            "last_modified_gmt": "1724926084",
            "last_touch": "1724926635",
            "group": "Folder",
            "url": "",
            "note": ""
          },
          {
            "id": "1234567891234567893",
            "name": "FSECRET2",
            "fullname": "FOLDER1/FSECRET2",
            "username": "",
            "password": "fsecret2",
            "last_modified_gmt": "1724926084",
            "last_touch": "1724926635",
            "group": "Folder",
            "url": "",
            "note": ""
          }
        ]
      JSON

    json = JSON.parse(shellunescape(run_command("fetch", "SECRET1", "FOLDER1/FSECRET1", "FOLDER1/FSECRET2")))

    expected_json = {
      "SECRET1"=>"secret1",
      "FOLDER1/FSECRET1"=>"fsecret1",
      "FOLDER1/FSECRET2"=>"fsecret2"
    }

    assert_equal expected_json, json
  end

  test "fetch with from" do
    stub_ticks.with("lpass --version 2> /dev/null")
    stub_ticks.with("lpass status --color never").returns("Logged in as [email protected].")

    stub_ticks
      .with("lpass show FOLDER1/FSECRET1 FOLDER1/FSECRET2 --json")
      .returns(<<~JSON)
        [
          {
            "id": "1234567891234567892",
            "name": "FSECRET1",
            "fullname": "FOLDER1/FSECRET1",
            "username": "",
            "password": "fsecret1",
            "last_modified_gmt": "1724926084",
            "last_touch": "1724926635",
            "group": "Folder",
            "url": "",
            "note": ""
          },
          {
            "id": "1234567891234567893",
            "name": "FSECRET2",
            "fullname": "FOLDER1/FSECRET2",
            "username": "",
            "password": "fsecret2",
            "last_modified_gmt": "1724926084",
            "last_touch": "1724926635",
            "group": "Folder",
            "url": "",
            "note": ""
          }
        ]
      JSON

    json = JSON.parse(shellunescape(run_command("fetch", "--from", "FOLDER1", "FSECRET1", "FSECRET2")))

    expected_json = {
      "FOLDER1/FSECRET1"=>"fsecret1",
      "FOLDER1/FSECRET2"=>"fsecret2"
    }

    assert_equal expected_json, json
  end

  test "fetch with signin" do
    stub_ticks.with("lpass --version 2> /dev/null")

    stub_ticks_with("lpass status --color never", succeed: false).returns("Not logged in.")
    stub_ticks_with("lpass login [email protected]", succeed: true).returns("")
    stub_ticks.with("lpass show SECRET1 --json").returns(single_item_json)

    json = JSON.parse(shellunescape(run_command("fetch", "SECRET1")))

    expected_json = {
      "SECRET1"=>"secret1"
    }

    assert_equal expected_json, json
  end

  test "fetch without CLI installed" do
    stub_ticks_with("lpass --version 2> /dev/null", succeed: false)

    error = assert_raises RuntimeError do
      JSON.parse(shellunescape(run_command("fetch", "SECRET1", "FOLDER1/FSECRET1", "FOLDER1/FSECRET2")))
    end
    assert_equal "LastPass CLI is not installed", error.message
  end

  private
    def run_command(*command)
      stdouted do
        Kamal::Cli::Secrets.start \
          [ *command,
            "-c", "test/fixtures/deploy_with_accessories.yml",
            "--adapter", "lastpass",
            "--account", "[email protected]" ]
      end
    end

    def single_item_json
      <<~JSON
        [
          {
            "id": "1234567891234567891",
            "name": "SECRET1",
            "fullname": "SECRET1",
            "username": "",
            "password": "secret1",
            "last_modified_gmt": "1724926054",
            "last_touch": "1724926639",
            "group": "",
            "url": "",
            "note": ""
          }
        ]
      JSON
    end
end