Skip to content

feat: cmd select environment file - #247

Open
htquangg wants to merge 2 commits into
jellydn:mainfrom
htquangg:feat/cmd-select-env-file
Open

feat: cmd select environment file#247
htquangg wants to merge 2 commits into
jellydn:mainfrom
htquangg:feat/cmd-select-env-file

Conversation

@htquangg

@htquangg htquangg commented Jun 30, 2025

Copy link
Copy Markdown

WHAT

New command HurlSelectEnvFile to easy select environment file

WHY

HOW

Screenshots (if appropriate):

Screenshot 2025-06-30 at 20 15 21

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • Linter
  • Tests
  • Review comments
  • Security

Important

Adds HurlSelectEnvFile command to hurl.nvim for selecting and registering environment files, with updates to configuration and documentation.

  • New Feature:
    • Adds HurlSelectEnvFile command in main.lua to select and register environment files.
    • Implements select_env_file() function in main.lua to prompt user for file selection.
  • Configuration:
    • Updates default_config in init.lua to include env_pattern for environment file matching.
  • Documentation:
    • Updates README.md and doc/hurl.nvim.txt to include usage instructions for HurlSelectEnvFile.
  • Utilities:
    • Adds find_env_files() function in utils.lua to locate environment files based on env_pattern.

This description was created by Ellipsis for 1be249e. You can customize this summary. It will automatically update as commits are pushed.

Summary by CodeRabbit

  • New Features

    • Added a new command to interactively select and register environment files matching a configurable pattern.
    • Introduced a configuration option to customize the pattern used for identifying environment variable files.
  • Documentation

    • Updated documentation to include the new command and configuration option, with expanded instructions and examples for environment file management.

@codesandbox

codesandbox Bot commented Jun 30, 2025

Copy link
Copy Markdown

Review or Edit in CodeSandbox

Open the branch in Web EditorVS CodeInsiders

Open Preview

@changeset-bot

changeset-bot Bot commented Jun 30, 2025

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 0819c7d

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Jun 30, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

The changes update documentation and code to introduce a new HurlSelectEnvFile command for interactively selecting environment files using a configurable pattern (env_pattern). The configuration and documentation are updated accordingly, and environment file registration logic is centralized in a new function.

Changes

Files / Grouped Files Change Summary
README.md, doc/hurl.nvim.txt Updated documentation to describe the new HurlSelectEnvFile command and the env_pattern option.
lua/hurl/init.lua Added the env_pattern option to the default configuration.
lua/hurl/main.lua Added register_env_file function, refactored HurlSetEnvFile to use it, and implemented the new HurlSelectEnvFile command with interactive selection logic.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Neovim
    participant HurlPlugin

    User->>Neovim: :HurlSelectEnvFile
    Neovim->>HurlPlugin: Call HurlSelectEnvFile command
    HurlPlugin->>HurlPlugin: Search for env files matching env_pattern
    alt Files found
        HurlPlugin->>User: Show selection prompt
        User->>HurlPlugin: Select env file
        HurlPlugin->>HurlPlugin: register_env_file(selected_path)
        HurlPlugin->>User: Notify env file registered
    else No files found
        HurlPlugin->>User: Notify no env files found
    end
Loading

Suggested reviewers

  • jellydn

Poem

A rabbit hops through code so bright,
Now env files are found with delight!
Choose with a click, no more to roam,
Patterns guide you safely home.
With docs refreshed and logic neat,
This update makes your workflow sweet!
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 27b50ed and 0819c7d.

📒 Files selected for processing (4)
  • README.md (3 hunks)
  • doc/hurl.nvim.txt (3 hunks)
  • lua/hurl/init.lua (1 hunks)
  • lua/hurl/main.lua (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • lua/hurl/init.lua
  • lua/hurl/main.lua
🧰 Additional context used
🪛 LanguageTool
README.md

[typographical] ~148-~148: Two consecutive commas
Context: ...g comma-separated values. Alternatively,, use the HurlSelectEnvFile command to ...

(DOUBLE_PUNCTUATION)

doc/hurl.nvim.txt

[uncategorized] ~194-~194: Possible missing article found.
Context: ...EnvFile` command to select and register environment file. NOTES - Ensure that the new env...

(AI_HYDRA_LEO_MISSING_THE)

✨ Finishing Touches
  • 📝 Docstrings were successfully generated. (🔄 Check again to generate docstrings again)- [ ] Create PR with Unit Tests
    ✅ Copyable Unit Test edits generated.
  • Create PR with Unit Tests
  • Commit Unit Tests in branch feat/cmd-select-env-file
  • Post Copyable Unit Tests in Comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @htquangg, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new command and configuration option to enhance the environment file selection process in the Hurl Neovim plugin. The HurlSelectEnvFile command provides an interactive way to choose environment files, while the env_pattern configuration allows users to customize the file search pattern.

Highlights

  • New Command: HurlSelectEnvFile: Introduces a new command HurlSelectEnvFile to simplify the selection of environment files for Hurl tests. This command allows users to interactively choose an environment file from a list of detected files.
  • Environment File Pattern: Adds a new configuration option env_pattern to customize the pattern used to find environment files. This allows users to specify a custom regex to match their environment file names.
  • Register Environment File Function: Introduces a new function register_env_file to register the selected environment file, splitting the path by commas to support multiple files.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@ellipsis-dev ellipsis-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Changes requested ❌

Reviewed everything up to 1be249e in 1 minute and 42 seconds. Click for details.
  • Reviewed 144 lines of code in 5 files
  • Skipped 0 files when reviewing.
  • Skipped posting 5 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. lua/hurl/main.lua:99
  • Draft comment:
    In the select_env_file function, consider handling the case when no environment files are found (empty list), to provide a clearer message to the user.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 20% vs. threshold = 50% 1. The comment points out a real edge case that isn't explicitly handled 2. vim.ui.select() behavior with empty list isn't clear from this code 3. However, this seems like a minor edge case - if no env files exist, nothing bad will happen 4. The comment isn't suggesting a critical bug fix, just a nice-to-have UX improvement 5. Without seeing utils.find_env_files() implementation, we can't be fully certain this is an issue I may be underestimating the importance of good UX around env file selection. A confusing UI when no files exist could frustrate users. While good UX is important, this seems like a minor edge case that wouldn't significantly impact users. The current behavior is likely fine - users will understand if no files appear in the selection UI. This comment suggests a minor UX improvement rather than fixing a real bug. Without more context about utils.find_env_files(), we can't be fully confident this needs fixing.
2. lua/hurl/main.lua:91
  • Draft comment:
    The register_env_file function splits the provided path by comma. Ensure that if future changes allow passing a table, the behavior remains consistent.
  • Reason this comment was not posted:
    Confidence changes required: 50% <= threshold 50% None
3. lua/hurl/utils.lua:355
  • Draft comment:
    find_env_files uses _HURL_GLOBAL_CONFIG.env_pattern for matching filenames. It might be safer to validate that env_pattern is non-nil, in case of misconfiguration.
  • Reason this comment was not posted:
    Confidence changes required: 50% <= threshold 50% None
4. lua/hurl/utils.lua:145
  • Draft comment:
    The function is_nightly returns true when nvim version is >= 0.11.0. The naming could be misleading since it's checking version, not whether the build is nightly. Consider a clearer name.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
5. lua/hurl/utils.lua:308
  • Draft comment:
    In get_storage_path, mkdir is called to create the storage directory. Adding error handling here (or logging if mkdir fails) could improve robustness.
  • Reason this comment was not posted:
    Confidence changes required: 40% <= threshold 50% None

Workflow ID: wflow_rS4hiziJ0KLhzuCu

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

Comment thread lua/hurl/main.lua Outdated
-- Select the env file
utils.create_cmd('HurlSelectEnvFile', function()
select_env_file()
end, { nargs = '*', range = true })

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a command that does not require arguments (HurlSelectEnvFile), consider using 'nargs = 0' instead of '*' to avoid unexpected parameter handling.

Suggested change
end, { nargs = '*', range = true })
end, { nargs = 0, range = true })

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new HurlSelectEnvFile command, allowing users to select an environment file. The changes include adding the new command, updating documentation, and adding a utility function. The review focuses on improving correctness and consistency.

Comment thread lua/hurl/utils.lua Outdated
Comment on lines +355 to +364
M.find_env_files = function()
return vim.fs.find(function(name, _)
return name:match(_HURL_GLOBAL_CONFIG.env_pattern)
end, {
path = vim.fn.expand('%:h'),
upward = true,
stop = vim.fn.getcwd(),
type = 'file',
limit = math.huge,
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The new find_env_files function introduces an inconsistency in how environment files are discovered within the plugin. It uses the new env_pattern configuration to find files, which is only used by the new HurlSelectEnvFile command. Other parts of the plugin, like the HurlManageVariable command, still use the older find_env_files_in_folders function, which has a different discovery logic and doesn't respect the new env_pattern. To ensure consistent behavior, unify the environment file discovery logic across the plugin, so that all commands that deal with environment files use this new find_env_files function and the env_pattern.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is valid point @htquangg Could you take a look? Thanks.

Comment thread README.md Outdated

To change the environment file name, use the `HurlSetEnvFile` command followed by the new file name. You can have multiple variable files by having comma-separated values.

Another way, use the `HurlSelectEnvFile` command to select and register environment file.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For better readability, consider rephrasing this sentence to be more clear.

Suggested change
Another way, use the `HurlSelectEnvFile` command to select and register environment file.
Alternatively, use the `HurlSelectEnvFile` command to select and register an environment file.

Comment thread doc/hurl.nvim.txt Outdated
by the new file name. You can have multiple variable files by having
comma-separated values.

Another way, use the `HurlSelectEnvFile` command to select and register environment file.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For better readability, consider rephrasing this sentence to be more clear.

Alternatively, use the `HurlSelectEnvFile` command to select and register an environment file.

Comment thread lua/hurl/main.lua Outdated
utils.notify('hurl: env file changed to ' .. updated_env, vim.log.levels.INFO)
end

---@return string? path

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The docstring for select_env_file is not accurate. This function is asynchronous and uses a callback; it does not directly return a value. The @return annotation is misleading and should be removed to accurately reflect the function's behavior.

---Selects an environment file using a UI prompt.

Comment thread lua/hurl/main.lua Outdated
-- Select the env file
utils.create_cmd('HurlSelectEnvFile', function()
select_env_file()
end, { nargs = '*', range = true })

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The options for the HurlSelectEnvFile command are not correct. Since this command takes no arguments and does not operate on a range, nargs = '*' and range = true are inappropriate. It should be defined with nargs = 0 to reflect that it takes no arguments.

  end, { nargs = 0 })

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 33ff81c and 27b50ed.

📒 Files selected for processing (5)
  • README.md (3 hunks)
  • doc/hurl.nvim.txt (3 hunks)
  • lua/hurl/init.lua (1 hunks)
  • lua/hurl/main.lua (2 hunks)
  • lua/hurl/utils.lua (1 hunks)
🔇 Additional comments (8)
lua/hurl/init.lua (1)

18-18: LGTM! Configuration pattern looks correct.

The env_pattern configuration uses proper Lua pattern syntax with escaped dot (%.) and is well-integrated into the default configuration.

README.md (3)

148-149: Clear documentation for the new command.

The addition properly documents the alternative method for selecting environment files.


314-314: Good addition to configuration documentation.

The env_pattern option is properly included in the default configuration example.


355-355: Helpful comment for the configuration option.

The inline comment clearly explains the purpose of the env_pattern setting.

doc/hurl.nvim.txt (2)

194-194: Consistent with README documentation.

The vim help file properly documents the new command functionality.


392-392: Configuration documentation is complete.

Both the default configuration and setup example properly include the new env_pattern option.

Also applies to: 433-433

lua/hurl/main.lua (2)

87-96: Good refactoring to centralize env file registration.

The function properly handles comma-separated paths and provides user feedback through logging and notifications.


173-180: Clean command implementation using helper functions.

Both commands properly utilize the helper functions and maintain consistency with the existing command patterns.

Comment thread lua/hurl/utils.lua Outdated
Comment on lines +352 to +365
---Find a list of environment files
---@see rest.Opts.Env.find
---@return string[] files Environment variable files path
M.find_env_files = function()
return vim.fs.find(function(name, _)
return name:match(_HURL_GLOBAL_CONFIG.env_pattern)
end, {
path = vim.fn.expand('%:h'),
upward = true,
stop = vim.fn.getcwd(),
type = 'file',
limit = math.huge,
})
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for pattern matching.

The function should handle cases where the pattern might be invalid or _HURL_GLOBAL_CONFIG.env_pattern is nil.

 M.find_env_files = function()
+  local pattern = _HURL_GLOBAL_CONFIG.env_pattern
+  if not pattern or type(pattern) ~= 'string' then
+    M.log_error('Invalid env_pattern configuration')
+    return {}
+  end
+  
   return vim.fs.find(function(name, _)
-    return name:match(_HURL_GLOBAL_CONFIG.env_pattern)
+    local ok, result = pcall(string.match, name, pattern)
+    return ok and result
   end, {
     path = vim.fn.expand('%:h'),
     upward = true,
     stop = vim.fn.getcwd(),
     type = 'file',
     limit = math.huge,
   })
 end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
---Find a list of environment files
---@see rest.Opts.Env.find
---@return string[] files Environment variable files path
M.find_env_files = function()
return vim.fs.find(function(name, _)
return name:match(_HURL_GLOBAL_CONFIG.env_pattern)
end, {
path = vim.fn.expand('%:h'),
upward = true,
stop = vim.fn.getcwd(),
type = 'file',
limit = math.huge,
})
end
---Find a list of environment files
---@see rest.Opts.Env.find
---@return string[] files Environment variable files path
M.find_env_files = function()
local pattern = _HURL_GLOBAL_CONFIG.env_pattern
if not pattern or type(pattern) ~= 'string' then
M.log_error('Invalid env_pattern configuration')
return {}
end
return vim.fs.find(function(name, _)
local ok, result = pcall(string.match, name, pattern)
return ok and result
end, {
path = vim.fn.expand('%:h'),
upward = true,
stop = vim.fn.getcwd(),
type = 'file',
limit = math.huge,
})
end
🤖 Prompt for AI Agents
In lua/hurl/utils.lua around lines 352 to 365, the function M.find_env_files
uses pattern matching without checking if _HURL_GLOBAL_CONFIG.env_pattern is
valid or nil. Add a check before the vim.fs.find call to ensure
_HURL_GLOBAL_CONFIG.env_pattern is a non-nil string. Also, wrap the pattern
matching in pcall or use xpcall to catch any errors from invalid patterns and
handle them gracefully, such as returning an empty list or logging an error.

Comment thread lua/hurl/main.lua Outdated
@htquangg
htquangg force-pushed the feat/cmd-select-env-file branch from 27b50ed to accb7b8 Compare June 30, 2025 14:12
coderabbitai Bot added a commit that referenced this pull request Jul 1, 2025
Docstrings generation was requested by @jellydn.

* #247 (comment)

The following files were modified:

* `lua/hurl/main.lua`
@coderabbitai

coderabbitai Bot commented Jul 1, 2025

Copy link
Copy Markdown
Contributor

Note

Generated docstrings for this pull request at #248

@coderabbitai

coderabbitai Bot commented Jul 1, 2025

Copy link
Copy Markdown
Contributor

Note

Unit test generation is an Early Access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai

coderabbitai Bot commented Jul 1, 2025

Copy link
Copy Markdown
Contributor

Here are the copyable unit test edits:

Copyable Edits

test/hurl_spec.lua

@@ -1,15 +1,570 @@
 ocal hurl = require('hurl.main')
 
 escribe('Hurl wrapper', function()
- it('should be able to load', function()
-   assert.truthy(hurl)
+ -- Setup and teardown
+ before_each(function()
+   -- Reset any global state or mocks
+   _HURL_GLOBAL_CONFIG = _HURL_GLOBAL_CONFIG or {}
+   vim.fn = vim.fn or {}
+   vim.api = vim.api or {}
+   vim.cmd = vim.cmd or function() end
+   vim.log = vim.log or { levels = { INFO = 1, WARN = 2, ERROR = 3 } }
+   vim.defer_fn = vim.defer_fn or function(fn, delay) fn() end
+   vim.ui = vim.ui or {}
+   vim.fs = vim.fs or {}
  end)
 
- it('should define a custom command: HurlRunner', function()
-   assert.truthy(vim.fn.exists(':HurlRunner'))
+ after_each(function()
+   -- Clean up any test state
+   _HURL_GLOBAL_CONFIG = {}
  end)
 
- it('should define a custom command: HurlRunnerAt', function()
-   assert.truthy(vim.fn.exists(':HurlRunnerAt'))
+ describe('module loading', function()
+   it('should be able to load', function()
+     assert.truthy(hurl)
+   end)
+
+   it('should be a table', function()
+     assert.is.table(hurl)
+   end)
+
+   it('should not be empty', function()
+     assert.is_not.same({}, hurl)
+   end)
+
+   it('should expose setup function', function()
+     assert.is.function(hurl.setup)
+   end)
+ end)
+
+ describe('setup function', function()
+   local original_create_cmd
+   local commands_created = {}
+
+   before_each(function()
+     commands_created = {}
+     -- Mock utils.create_cmd to track command creation
+     local utils = require('hurl.utils')
+     original_create_cmd = utils.create_cmd
+     utils.create_cmd = function(name, fn, opts)
+       commands_created[name] = { fn = fn, opts = opts }
+       vim.fn['exists'] = function(cmd)
+         return ':' .. name == cmd and 1 or 0
+       end
+     end
+   end)
+
+   after_each(function()
+     if original_create_cmd then
+       local utils = require('hurl.utils')
+       utils.create_cmd = original_create_cmd
+     end
+   end)
+
+   it('should create all required commands', function()
+     hurl.setup()
+     
+     local expected_commands = {
+       'HurlRunner',
+       'HurlRunnerAt',
+       'HurlRunnerToEntry',
+       'HurlToggleMode',
+       'HurlSetEnvFile',
+       'HurlSelectEnvFile',
+       'HurlVerbose',
+       'HurlVeryVerbose',
+       'HurlJson',
+       'HurlSetVariable',
+       'HurlManageVariable',
+       'HurlDebugInfo',
+       'HurlShowLastResponse',
+       'HurlRunnerToEnd',
+       'HurlRerun'
+     }
+
+     for _, cmd in ipairs(expected_commands) do
+       assert.truthy(commands_created[cmd], 'Command ' .. cmd .. ' should be created')
+     end
+   end)
+
+   it('should set up codelens', function()
+     local codelens = require('hurl.codelens')
+     local setup_called = false
+     codelens.setup = function() setup_called = true end
+     
+     hurl.setup()
+     
+     assert.is_true(setup_called)
+   end)
+
+   it('should handle setup multiple times', function()
+     assert.has_no.errors(function()
+       hurl.setup()
+       hurl.setup()
+       hurl.setup()
+     end)
+   end)
+ end)
+
+ describe('custom commands functionality', function()
+   local mock_utils, mock_http, mock_hurl_runner
+   
+   before_each(function()
+     -- Mock dependencies
+     mock_utils = {
+       create_cmd = function(name, fn, opts) 
+         vim.fn['exists'] = function(cmd) return ':' .. name == cmd and 1 or 0 end
+       end,
+       get_visual_selection = function() return {'GET https://example.com'} end,
+       create_tmp_file = function() return '/tmp/test.hurl' end,
+       log_info = function() end,
+       log_warn = function() end,
+       notify = function() end,
+       is_nightly = function() return false end,
+       is_hurl_parser_available = false
+     }
+     
+     mock_http = {
+       find_hurl_entry_positions_in_buffer = function()
+         return {current = 1, start_line = 1, end_line = 5}
+       end,
+       find_http_verb_positions_in_buffer = function()
+         return {current = 1, start_line = 1, end_line = 5}
+       end
+     }
+     
+     mock_hurl_runner = {
+       execute_hurl_cmd = function() end,
+       run_hurl_verbose = function() end
+     }
+
+     -- Mock vim functions
+     vim.fn.expand = function(arg)
+       if arg == '%:p' then return '/path/to/test.hurl' end
+       if arg == '%:h' then return '/path/to' end
+       return arg
+     end
+     
+     vim.fn.getcwd = function() return '/project' end
+     vim.fn.filereadable = function() return 1 end
+
+     -- Replace requires
+     package.loaded['hurl.utils'] = mock_utils
+     package.loaded['hurl.http_utils'] = mock_http
+     package.loaded['hurl.lib.hurl_runner'] = mock_hurl_runner
+   end)
+
+   describe('HurlRunner command', function()
+     it('should handle file execution', function()
+       local execute_called = false
+       mock_hurl_runner.execute_hurl_cmd = function(opts)
+         execute_called = true
+         assert.truthy(opts)
+         assert.is.table(opts)
+       end
+
+       hurl.setup()
+       local cmd_fn = commands_created['HurlRunner'].fn
+       cmd_fn({range = 0, fargs = {}})
+       
+       assert.is_true(execute_called)
+     end)
+
+     it('should handle selection execution', function()
+       local execute_called = false
+       mock_hurl_runner.execute_hurl_cmd = function(opts)
+         execute_called = true
+         assert.truthy(opts)
+       end
+
+       hurl.setup()
+       local cmd_fn = commands_created['HurlRunner'].fn
+       cmd_fn({range = 1, fargs = {}})
+       
+       assert.is_true(execute_called)
+     end)
+
+     it('should handle empty selection gracefully', function()
+       mock_utils.get_visual_selection = function() return nil end
+       
+       hurl.setup()
+       local cmd_fn = commands_created['HurlRunner'].fn
+       
+       assert.has_no.errors(function()
+         cmd_fn({range = 1, fargs = {}})
+       end)
+     end)
+
+     it('should handle tmp file creation failure', function()
+       mock_utils.create_tmp_file = function() return nil end
+       
+       hurl.setup()
+       local cmd_fn = commands_created['HurlRunner'].fn
+       
+       assert.has_no.errors(function()
+         cmd_fn({range = 1, fargs = {}})
+       end)
+     end)
+   end)
+
+   describe('HurlRunnerAt command', function()
+     it('should execute at current line when HTTP method found', function()
+       local execute_called = false
+       mock_hurl_runner.execute_hurl_cmd = function(opts)
+         execute_called = true
+         assert.truthy(opts)
+         assert.is.table(opts)
+       end
+
+       hurl.setup()
+       local cmd_fn = commands_created['HurlRunnerAt'].fn
+       cmd_fn({fargs = {}})
+       
+       assert.is_true(execute_called)
+     end)
+
+     it('should handle no HTTP method found', function()
+       mock_http.find_hurl_entry_positions_in_buffer = function()
+         return {current = 0, start_line = nil, end_line = nil}
+       end
+       mock_http.find_http_verb_positions_in_buffer = function()
+         return {current = 0, start_line = nil, end_line = nil}
+       end
+
+       hurl.setup()
+       local cmd_fn = commands_created['HurlRunnerAt'].fn
+       
+       assert.has_no.errors(function()
+         cmd_fn({fargs = {}})
+       end)
+     end)
+   end)
+
+   describe('HurlToggleMode command', function()
+     it('should toggle from split to popup', function()
+       _HURL_GLOBAL_CONFIG.mode = 'split'
+       
+       hurl.setup()
+       local cmd_fn = commands_created['HurlToggleMode'].fn
+       cmd_fn()
+       
+       assert.is.equal('popup', _HURL_GLOBAL_CONFIG.mode)
+     end)
+
+     it('should toggle from popup to split', function()
+       _HURL_GLOBAL_CONFIG.mode = 'popup'
+       
+       hurl.setup()
+       local cmd_fn = commands_created['HurlToggleMode'].fn
+       cmd_fn()
+       
+       assert.is.equal('split', _HURL_GLOBAL_CONFIG.mode)
+     end)
+
+     it('should handle undefined mode', function()
+       _HURL_GLOBAL_CONFIG.mode = nil
+       
+       hurl.setup()
+       local cmd_fn = commands_created['HurlToggleMode'].fn
+       
+       assert.has_no.errors(function()
+         cmd_fn()
+       end)
+       
+       assert.is.equal('popup', _HURL_GLOBAL_CONFIG.mode)
+     end)
+   end)
+
+   describe('HurlSetEnvFile command', function()
+     it('should set environment file', function()
+       hurl.setup()
+       local cmd_fn = commands_created['HurlSetEnvFile'].fn
+       cmd_fn({fargs = {'.env.test'}})
+       
+       assert.is.table(_HURL_GLOBAL_CONFIG.env_file)
+       assert.is.equal('.env.test', _HURL_GLOBAL_CONFIG.env_file[1])
+     end)
+
+     it('should handle multiple environment files', function()
+       hurl.setup()
+       local cmd_fn = commands_created['HurlSetEnvFile'].fn
+       cmd_fn({fargs = {'.env.test,.env.local'}})
+       
+       assert.is.table(_HURL_GLOBAL_CONFIG.env_file)
+       assert.is.equal(2, #_HURL_GLOBAL_CONFIG.env_file)
+     end)
+
+     it('should handle no environment file provided', function()
+       hurl.setup()
+       local cmd_fn = commands_created['HurlSetEnvFile'].fn
+       
+       assert.has_no.errors(function()
+         cmd_fn({fargs = {}})
+       end)
+     end)
+   end)
+
+   describe('HurlSetVariable command', function()
+     it('should set global variable', function()
+       hurl.setup()
+       local cmd_fn = commands_created['HurlSetVariable'].fn
+       cmd_fn({fargs = {'test_var', 'test_value'}})
+       
+       assert.is.table(_HURL_GLOBAL_CONFIG.global_vars)
+       assert.is.equal('test_value', _HURL_GLOBAL_CONFIG.global_vars['test_var'])
+     end)
+
+     it('should handle missing variable name', function()
+       hurl.setup()
+       local cmd_fn = commands_created['HurlSetVariable'].fn
+       
+       assert.has_no.errors(function()
+         cmd_fn({fargs = {}})
+       end)
+     end)
+
+     it('should handle missing variable value', function()
+       hurl.setup()
+       local cmd_fn = commands_created['HurlSetVariable'].fn
+       
+       assert.has_no.errors(function()
+         cmd_fn({fargs = {'test_var'}})
+       end)
+     end)
+   end)
+
+   describe('HurlVerbose and HurlVeryVerbose commands', function()
+     it('should run verbose command with current entry', function()
+       local verbose_called = false
+       mock_hurl_runner.run_hurl_verbose = function(filePath, fromEntry, toEntry, isVeryVerbose)
+         verbose_called = true
+         assert.is.string(filePath)
+         assert.is.number(fromEntry)
+         assert.is.number(toEntry)
+         assert.is.boolean(isVeryVerbose)
+       end
+
+       hurl.setup()
+       local cmd_fn = commands_created['HurlVerbose'].fn
+       cmd_fn({fargs = {}})
+       
+       assert.is_true(verbose_called)
+     end)
+
+     it('should run very verbose command', function()
+       local very_verbose_called = false
+       mock_hurl_runner.run_hurl_verbose = function(filePath, fromEntry, toEntry, isVeryVerbose)
+         very_verbose_called = true
+         assert.is_true(isVeryVerbose)
+       end
+
+       hurl.setup()
+       local cmd_fn = commands_created['HurlVeryVerbose'].fn
+       cmd_fn({fargs = {}})
+       
+       assert.is_true(very_verbose_called)
+     end)
+
+     it('should handle custom entry range', function()
+       local verbose_called = false
+       mock_hurl_runner.run_hurl_verbose = function(filePath, fromEntry, toEntry, isVeryVerbose)
+         verbose_called = true
+         assert.is.equal(2, fromEntry)
+         assert.is.equal(5, toEntry)
+       end
+
+       hurl.setup()
+       local cmd_fn = commands_created['HurlVerbose'].fn
+       cmd_fn({fargs = {'2', '5'}})
+       
+       assert.is_true(verbose_called)
+     end)
+   end)
+
+   describe('HurlRerun command', function()
+     it('should rerun last command', function()
+       -- First run a command to set last_from_entry
+       hurl.setup()
+       local run_at_cmd = commands_created['HurlRunnerAt'].fn
+       run_at_cmd({fargs = {}})
+       
+       -- Then test rerun
+       local rerun_called = false
+       mock_hurl_runner.execute_hurl_cmd = function(opts)
+         rerun_called = true
+         assert.truthy(opts)
+       end
+       
+       local cmd_fn = commands_created['HurlRerun'].fn
+       cmd_fn()
+       
+       assert.is_true(rerun_called)
+     end)
+
+     it('should handle no previous command', function()
+       hurl.setup()
+       local cmd_fn = commands_created['HurlRerun'].fn
+       
+       assert.has_no.errors(function()
+         cmd_fn()
+       end)
+     end)
+   end)
+ end)
+
+ describe('error handling and edge cases', function()
+   before_each(function()
+     vim.fn.expand = function() error('File system error') end
+   end)
+
+   it('should handle file system errors gracefully', function()
+     assert.has_no.errors(function()
+       hurl.setup()
+     end)
+   end)
+
+   it('should handle missing dependencies gracefully', function()
+     -- Temporarily break require
+     local original_require = require
+     require = function(module)
+       if module:match('hurl%.') then
+         error('Module not found: ' .. module)
+       end
+       return original_require(module)
+     end
+
+     assert.has_no.errors(function()
+       pcall(hurl.setup)
+     end)
+
+     -- Restore require
+     require = original_require
+   end)
+ end)
+
+ describe('integration scenarios', function()
+   before_each(function()
+     -- Set up complete mock environment
+     local utils = require('hurl.utils')
+     utils.create_cmd = function(name, fn, opts) 
+       vim.fn['exists'] = function(cmd) return ':' .. name == cmd and 1 or 0 end
+     end
+     utils.get_visual_selection = function() 
+       return {
+         'GET https://api.example.com/users',
+         'Accept: application/json',
+         'Authorization: Bearer {{token}}'
+       }
+     end
+     utils.create_tmp_file = function() return '/tmp/hurl_test.hurl' end
+     utils.log_info = function() end
+     utils.notify = function() end
+   end)
+
+   it('should handle complex hurl workflow', function()
+     hurl.setup()
+     
+     -- Set a variable
+     local set_var_cmd = commands_created['HurlSetVariable'].fn
+     set_var_cmd({fargs = {'base_url', 'https://api.example.com'}})
+     
+     -- Toggle mode
+     local toggle_cmd = commands_created['HurlToggleMode'].fn
+     toggle_cmd()
+     
+     -- Run a request
+     local run_cmd = commands_created['HurlRunner'].fn
+     
+     assert.has_no.errors(function()
+       run_cmd({range = 0, fargs = {}})
+     end)
+   end)
+
+   it('should handle environment file workflow', function()
+     hurl.setup()
+     
+     -- Set environment file
+     local env_cmd = commands_created['HurlSetEnvFile'].fn
+     env_cmd({fargs = {'.env.test'}})
+     
+     -- Set variables
+     local var_cmd = commands_created['HurlSetVariable'].fn
+     var_cmd({fargs = {'api_key', 'test123'}})
+     var_cmd({fargs = {'base_url', 'https://test.api.com'}})
+     
+     assert.is.equal('.env.test', _HURL_GLOBAL_CONFIG.env_file[1])
+     assert.is.equal('test123', _HURL_GLOBAL_CONFIG.global_vars['api_key'])
+     assert.is.equal('https://test.api.com', _HURL_GLOBAL_CONFIG.global_vars['base_url'])
+   end)
+ end)
+
+ describe('performance and stress tests', function()
+   it('should handle rapid command execution', function()
+     hurl.setup()
+     
+     local start_time = os.clock()
+     
+     for i = 1, 100 do
+       local toggle_cmd = commands_created['HurlToggleMode'].fn
+       toggle_cmd()
+     end
+     
+     local end_time = os.clock()
+     assert.is_true((end_time - start_time) < 1, 'Should complete toggles within 1 second')
+   end)
+
+   it('should handle many variable assignments', function()
+     hurl.setup()
+     
+     local start_time = os.clock()
+     local var_cmd = commands_created['HurlSetVariable'].fn
+     
+     for i = 1, 1000 do
+       var_cmd({fargs = {'var' .. i, 'value' .. i}})
+     end
+     
+     local end_time = os.clock()
+     assert.is_true((end_time - start_time) < 2, 'Should complete variable assignments within 2 seconds')
+     assert.is.equal(1000, vim.tbl_count(_HURL_GLOBAL_CONFIG.global_vars))
+   end)
+ end)
+
+ -- Legacy tests (keeping for compatibility)
+ describe('command existence (legacy)', function()
+   before_each(function()
+     hurl.setup()
+   end)
+
+   it('should define a custom command: HurlRunner', function()
+     assert.truthy(vim.fn.exists(':HurlRunner'))
+   end)
+
+   it('should define a custom command: HurlRunnerAt', function()
+     assert.truthy(vim.fn.exists(':HurlRunnerAt'))
+   end)
+
+   it('should define a custom command: HurlToggleMode', function()
+     assert.truthy(vim.fn.exists(':HurlToggleMode'))
+   end)
+
+   it('should define a custom command: HurlVerbose', function()
+     assert.truthy(vim.fn.exists(':HurlVerbose'))
+   end)
+
+   it('should define a custom command: HurlVeryVerbose', function()
+     assert.truthy(vim.fn.exists(':HurlVeryVerbose'))
+   end)
+
+   it('should define a custom command: HurlSetEnvFile', function()
+     assert.truthy(vim.fn.exists(':HurlSetEnvFile'))
+   end)
+
+   it('should define a custom command: HurlSetVariable', function()
+     assert.truthy(vim.fn.exists(':HurlSetVariable'))
+   end)
+
+   it('should define a custom command: HurlRerun', function()
+     assert.truthy(vim.fn.exists(':HurlRerun'))
+   end)
  end)
 nd)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants