Skip to content

Latest commit

 

History

History
144 lines (113 loc) · 5.22 KB

File metadata and controls

144 lines (113 loc) · 5.22 KB

condaUtils Testing Implementation Summary

Overview

This document summarizes the implementation of unit tests for src/managers/conda/condaUtils.ts based on the test plan in docs/condaUtils-test-plan.md.

Tests Implemented (17 tests)

1. Configuration and Settings (4 tests)

  • getCondaPathSetting(): Tests configuration reading with tilde expansion
    • Returns conda path from python.condaPath setting
    • Untildifies paths with tilde
    • Returns undefined when not set
    • Handles non-string values

2. Version Utilities (6 tests)

  • trimVersionToMajorMinor(): Tests version string parsing
    • Trims to major.minor.patch format
    • Handles extra segments
    • Handles two-part versions
    • Returns original for single-part versions
    • Returns original for non-standard versions
    • Handles version with leading 'v'

3. Name and Path Utilities (7 tests)

  • getName(): Tests URI to project name conversion

    • Returns undefined when no URIs provided
    • Returns undefined for empty array
    • Returns undefined for multiple URIs
    • Returns project name for single URI
    • Returns project name for single-element array
    • Returns undefined when project not found
  • generateName(): Tests unique name generation

    • Generates unique name with env_ prefix

Testing Challenges

Node.js Module Stubbing

Many functions in condaUtils.ts directly use Node.js built-in modules:

  • fs-extra (pathExists, readdir, readJsonSync, etc.)
  • child_process (spawn)
  • which module

Issue: Modern versions of these modules cannot be stubbed directly with Sinon due to non-configurable property descriptors.

Current Solutions:

  1. Focus on pure functions that don't require module mocking
  2. Use integration-style tests where feasible
  3. Test functions that only depend on wrapper APIs (workspace.apis)

Functions Requiring Wrapper Abstractions

The following functions from the test plan require wrapper functions to be testable:

Conda Resolution & Execution

  • getCondaExecutable() - uses fse.pathExists, which, spawn
  • getConda() - delegates to getCondaExecutable()
  • _runConda() - uses ch.spawn
  • runCondaExecutable() - uses getCondaExecutable() and spawn

Prefix Discovery & Defaults

  • getCondaInfo() - uses runConda()
  • getPrefixes() - uses getCondaInfo()
  • getDefaultCondaPrefix() - uses getPrefixes()

Version Metadata

  • getVersion() - uses fse.readdir, fse.readJsonSync

Shell Activation Maps

  • buildShellActivationMapForConda() - complex logic with file operations
  • generateShellActivationMapFromConfig() - testable (pure function)
  • windowsExceptionGenerateConfig() - uses getCondaHookPs1Path()

Environment Info Builders

  • getNamedCondaPythonInfo() - uses buildShellActivationMapForConda()
  • getPrefixesCondaPythonInfo() - uses buildShellActivationMapForConda()
  • nativeToPythonEnv() - uses info builders

Path Resolution & Refresh

  • resolveCondaPath() - uses native finder
  • refreshCondaEnvs() - uses getConda(), native finder

Environment Creation & Deletion

  • createNamedCondaEnvironment() - uses runCondaExecutable(), file operations
  • createPrefixCondaEnvironment() - uses runCondaExecutable(), file operations
  • quickCreateConda() - uses runCondaExecutable()
  • deleteCondaEnvironment() - uses runCondaExecutable()

Package Management

  • refreshPackages() - uses runCondaExecutable()
  • managePackages() - uses runCondaExecutable()
  • getCommonPackages() - uses fse.readFile
  • selectCommonPackagesOrSkip() - testable (UI logic)
  • getCommonCondaPackagesToInstall() - uses getCommonPackages()

No-Python Handling

  • installPython() - uses runCondaExecutable()
  • checkForNoPythonCondaEnvironment() - uses installPython()

Recommendations for Future Testing

Option 1: Create Wrapper Abstractions

Create abstraction layer for file system and process operations:

// src/managers/conda/condaWrappers.ts
export interface FileSystemOperations {
    pathExists(path: string): Promise<boolean>;
    readdir(path: string): Promise<string[]>;
    readJsonSync(path: string): any;
}

export interface ProcessOperations {
    spawn(command: string, args: string[], options: any): ChildProcess;
}

This would allow:

  • Easy mocking in unit tests
  • Better separation of concerns
  • Improved testability

Option 2: Integration Tests

Use VS Code's extension test framework for integration tests that:

  • Run actual conda commands (when conda is available)
  • Test full workflows
  • Validate end-to-end behavior

Option 3: Hybrid Approach

  • Unit tests for pure functions (current implementation)
  • Wrapper abstractions for I/O operations
  • Integration tests for critical paths

Test Results

All 257 tests passing (17 new condaUtils tests added):

condaUtils - Configuration and Settings (4 tests)
condaUtils - Version Utilities (6 tests)
condaUtils - Name and Path Utilities (7 tests)

Conclusion

This initial implementation provides test coverage for the pure, easily testable functions in condaUtils. To achieve comprehensive coverage as outlined in the test plan, the codebase would benefit from wrapper abstractions around Node.js modules or a shift toward integration testing for I/O-heavy functions.