# This file is part of sbs # # Copyright (C) 2025 Steve Milner # # SPDX-License-Identifier: GPL-3.0-or-later # Generated by Gemini 2.5 Flash import pytest import csv from unittest.mock import patch, mock_open from sbs.ds.card import Card from sbs.ds.csvexp import csvexp def test_csvexp_reads_single_card(): """ Test that csvexp correctly reads a single row from a CSV and yields one Card object. """ # Simulate CSV content as a string csv_content = ( "Issue key,Assignee,Summary,Description,Custom field (Story Points)\n" "PROJ-1,John Doe,Task 1,Desc 1,5\n" ) # Use patch.object to mock csv.DictReader's __iter__ method # and mock_open to simulate file reading with patch("builtins.open", mock_open(read_data=csv_content)) as mock_file: # Mock csv.DictReader. We'll manually specify its behavior. # When csv.DictReader is called, it returns an iterable. # We need to ensure that iterable provides the dictionary rows. mock_reader_instance = iter( [ { "Issue key": "PROJ-1", "Assignee": "John Doe", "Summary": "Task 1", "Description": "Desc 1", "Custom field (Story Points)": "5", } ] ) with patch( "csv.DictReader", return_value=mock_reader_instance ) as mock_dict_reader: cards = list(csvexp("dummy.csv")) # Assertions mock_file.assert_called_once_with( "dummy.csv", "r" ) # Check if open was called mock_dict_reader.assert_called_once_with( mock_file() ) # Check if DictReader was called with the file handle assert len(cards) == 1 card = cards[0] assert isinstance(card, Card) assert card.key == "PROJ-1" assert card.assignee == "John Doe" assert card.summary == "Task 1" assert card.description == "Desc 1" assert card.story_points == "5" # Story points are read as string from CSV def test_csvexp_reads_multiple_cards(): """ Test that csvexp correctly reads multiple rows from a CSV and yields multiple Card objects. """ csv_content = ( "Issue key,Assignee,Summary,Description,Custom field (Story Points)\n" "PROJ-1,John Doe,Task 1,Desc 1,5\n" "PROJ-2,Jane Smith,Task 2,Desc 2,3\n" "PROJ-3,Peter Jones,Task 3,Desc 3,8\n" ) with patch("builtins.open", mock_open(read_data=csv_content)) as mock_file: mock_reader_instance = iter( [ { "Issue key": "PROJ-1", "Assignee": "John Doe", "Summary": "Task 1", "Description": "Desc 1", "Custom field (Story Points)": "5", }, { "Issue key": "PROJ-2", "Assignee": "Jane Smith", "Summary": "Task 2", "Description": "Desc 2", "Custom field (Story Points)": "3", }, { "Issue key": "PROJ-3", "Assignee": "Peter Jones", "Summary": "Task 3", "Description": "Desc 3", "Custom field (Story Points)": "8", }, ] ) with patch("csv.DictReader", return_value=mock_reader_instance): cards = list(csvexp("multiple_cards.csv")) assert len(cards) == 3 assert cards[0].key == "PROJ-1" assert cards[1].assignee == "Jane Smith" assert cards[2].story_points == "8" def test_csvexp_handles_empty_csv(): """ Test that csvexp returns an empty list when the CSV file is empty (only header or no data). """ csv_content = "Issue key,Assignee,Summary,Description,Custom field (Story Points)\n" with patch("builtins.open", mock_open(read_data=csv_content)) as mock_file: # csv.DictReader on an empty file (after header) will yield nothing mock_reader_instance = iter([]) with patch("csv.DictReader", return_value=mock_reader_instance): cards = list(csvexp("empty.csv")) assert len(cards) == 0 def test_csvexp_handles_missing_file(): """ Test that csvexp raises a FileNotFoundError if the specified CSV file does not exist. """ with patch("builtins.open", side_effect=FileNotFoundError): with pytest.raises(FileNotFoundError): list(csvexp("non_existent.csv")) def test_csvexp_handles_missing_column(): """ Test that csvexp raises a KeyError if a required column is missing from the CSV. """ csv_content = ( "Issue key,Assignee,Summary,Description,Missing Column\n" "PROJ-1,John Doe,Task 1,Desc 1,5\n" ) with patch("builtins.open", mock_open(read_data=csv_content)) as mock_file: # Simulate DictReader providing a row missing a key mock_reader_instance = iter( [ { "Issue key": "PROJ-1", "Assignee": "John Doe", "Summary": "Task 1", "Description": "Desc 1", "Missing Column": "5", } # Missing "Custom field (Story Points)" ] ) with patch("csv.DictReader", return_value=mock_reader_instance): with pytest.raises(KeyError): list(csvexp("missing_column.csv")) def test_csvexp_ignore_parameter(): """ Test that the `ignore` parameter is correctly accepted and does not interfere. """ csv_content = ( "Issue key,Assignee,Summary,Description,Custom field (Story Points)\n" "PROJ-1,John Doe,Task 1,Desc 1,5\n" ) with patch("builtins.open", mock_open(read_data=csv_content)) as mock_file: mock_reader_instance = iter( [ { "Issue key": "PROJ-1", "Assignee": "John Doe", "Summary": "Task 1", "Description": "Desc 1", "Custom field (Story Points)": "5", } ] ) with patch("csv.DictReader", return_value=mock_reader_instance): # Pass an extra keyword argument that should be ignored cards = list(csvexp("dummy.csv", extra_param="value")) assert len(cards) == 1 assert cards[0].key == "PROJ-1"