Fix CI failures: Add missing Elixir test repository source files

The lib/ directory was being ignored by the root .gitignore Python packaging rule.
This caused CI failures because test files like models.ex, services.ex, etc. were
missing from the repository.

- Add exception to .gitignore for test/resources/repos/elixir/test_repo/lib
- Add all missing Elixir source files:
  - examples.ex (example functions and data structures)
  - models.ex (User struct and related functions)
  - services.ex (UserService module)
  - test_repo.ex (main module)
  - utils.ex (utility functions)
  - ignored_dir/ignored_module.ex (for testing directory filtering)

These files are essential for Elixir language server tests to function properly.
This commit is contained in:
David Bernazal
2025-07-01 07:33:57 -05:00
parent 75016674e1
commit e9378ecf79
7 changed files with 738 additions and 0 deletions
+2
View File
@@ -224,3 +224,5 @@ tmp/
# Elixir
/test/resources/repos/elixir/test_repo/deps
# Exception: Don't ignore Elixir test repository lib directory (contains source code)
!/test/resources/repos/elixir/test_repo/lib
@@ -0,0 +1,211 @@
defmodule TestRepo.Examples do
@moduledoc """
Examples module demonstrating usage of models and services.
Similar to Python's examples directory, this shows how different modules work together.
"""
alias TestRepo.Models.{User, Item}
alias TestRepo.Services.{UserService, ItemService, OrderService}
defmodule UserManagement do
@doc """
Creates a complete user workflow example.
"""
def run_user_example do
# Start user service
{:ok, user_service} = UserService.start_link()
# Create users
{:ok, alice} = UserService.create_user(user_service, "1", "Alice", "alice@example.com", ["admin"])
{:ok, bob} = UserService.create_user(user_service, "2", "Bob", "bob@example.com", ["user"])
# Get users
{:ok, retrieved_alice} = UserService.get_user(user_service, "1")
# List all users
all_users = UserService.list_users(user_service)
# Clean up
GenServer.stop(user_service)
%{
created_alice: alice,
created_bob: bob,
retrieved_alice: retrieved_alice,
all_users: all_users
}
end
@doc """
Demonstrates user role management.
"""
def manage_user_roles do
user = User.new("role_user", "Role User", "role@example.com")
# Add roles
user_with_admin = User.add_role(user, "admin")
user_with_multiple = User.add_role(user_with_admin, "moderator")
# Check roles
has_admin = User.has_role?(user_with_multiple, "admin")
has_guest = User.has_role?(user_with_multiple, "guest")
%{
original_user: user,
user_with_roles: user_with_multiple,
has_admin: has_admin,
has_guest: has_guest
}
end
end
defmodule ShoppingExample do
@doc """
Creates a complete shopping workflow.
"""
def run_shopping_example do
# Create user and items
user = User.new("customer1", "Customer One", "customer@example.com")
item1 = Item.new("widget1", "Super Widget", 19.99, "electronics")
item2 = Item.new("gadget1", "Cool Gadget", 29.99, "electronics")
# Create order
order = OrderService.create_order("order1", user)
# Add items to order
order_with_item1 = OrderService.add_item_to_order(order, item1)
order_with_items = OrderService.add_item_to_order(order_with_item1, item2)
# Process the order
processed_order = OrderService.process_order(order_with_items)
completed_order = OrderService.complete_order(processed_order)
%{
user: user,
items: [item1, item2],
final_order: completed_order,
total_cost: completed_order.total
}
end
@doc """
Demonstrates item filtering and searching.
"""
def item_filtering_example do
# Start item service
{:ok, item_service} = ItemService.start_link()
# Create various items
ItemService.create_item(item_service, "laptop", "Gaming Laptop", 1299.99, "electronics")
ItemService.create_item(item_service, "book", "Elixir Guide", 39.99, "books")
ItemService.create_item(item_service, "phone", "Smartphone", 699.99, "electronics")
ItemService.create_item(item_service, "novel", "Great Novel", 19.99, "books")
# Get all items
all_items = ItemService.list_items(item_service)
# Filter by category
electronics = ItemService.list_items(item_service, "electronics")
books = ItemService.list_items(item_service, "books")
# Clean up
Agent.stop(item_service)
%{
all_items: all_items,
electronics: electronics,
books: books,
total_items: length(all_items),
electronics_count: length(electronics),
books_count: length(books)
}
end
end
defmodule IntegrationExample do
@doc """
Runs a complete e-commerce scenario.
"""
def run_full_scenario do
# Setup services
container = TestRepo.Services.create_service_container()
TestRepo.Services.setup_sample_data(container)
# Get sample data
{:ok, sample_user} = UserService.get_user(container.user_service, TestRepo.Services.sample_user_id())
{:ok, sample_item} = ItemService.get_item(container.item_service, TestRepo.Services.sample_item_id())
# Create additional items
{:ok, premium_item} = ItemService.create_item(
container.item_service,
"premium",
"Premium Product",
99.99,
"premium"
)
# Create order with multiple items
order = OrderService.create_order("big_order", sample_user, [sample_item])
order_with_premium = OrderService.add_item_to_order(order, premium_item)
# Process through order lifecycle
processing_order = OrderService.process_order(order_with_premium)
final_order = OrderService.complete_order(processing_order)
# Serialize everything for output
serialized_user = TestRepo.Services.serialize_model(sample_user)
serialized_order = TestRepo.Services.serialize_model(final_order)
# Clean up
GenServer.stop(container.user_service)
Agent.stop(container.item_service)
%{
scenario: "full_ecommerce",
user: serialized_user,
order: serialized_order,
total_revenue: final_order.total,
items_sold: length(final_order.items)
}
end
@doc """
Demonstrates error handling scenarios.
"""
def error_handling_example do
{:ok, user_service} = UserService.start_link()
# Try to create duplicate user
{:ok, _user1} = UserService.create_user(user_service, "dup", "User", "user@example.com")
duplicate_result = UserService.create_user(user_service, "dup", "Another User", "another@example.com")
# Try to get non-existent user
missing_user_result = UserService.get_user(user_service, "nonexistent")
# Try to delete non-existent user
delete_result = UserService.delete_user(user_service, "nonexistent")
GenServer.stop(user_service)
%{
duplicate_user_error: duplicate_result,
missing_user_error: missing_user_result,
delete_missing_error: delete_result
}
end
end
@doc """
Main function to run all examples.
"""
def run_all_examples do
%{
user_management: UserManagement.run_user_example(),
role_management: UserManagement.manage_user_roles(),
shopping: ShoppingExample.run_shopping_example(),
item_filtering: ShoppingExample.item_filtering_example(),
integration: IntegrationExample.run_full_scenario(),
error_handling: IntegrationExample.error_handling_example()
}
end
end
@@ -0,0 +1,23 @@
defmodule TestRepo.IgnoredDir.IgnoredModule do
@moduledoc """
This module is in a directory that should be ignored by the language server.
It's used for testing directory filtering functionality.
"""
alias TestRepo.Models.User
@doc """
This function references the User model to test that ignored directories
don't show up in symbol references.
"""
def create_ignored_user do
User.new("ignored", "Ignored User", "ignored@example.com")
end
@doc """
Another function that uses models.
"""
def process_ignored_user(user) do
User.add_role(user, "ignored_role")
end
end
@@ -0,0 +1,166 @@
defmodule TestRepo.Models do
@moduledoc """
Models module demonstrating various Elixir patterns including structs, protocols, and behaviours.
"""
defprotocol Serializable do
@doc "Convert model to map representation"
def to_map(model)
end
defmodule User do
@type t :: %__MODULE__{
id: String.t(),
name: String.t() | nil,
email: String.t(),
roles: list(String.t())
}
defstruct [:id, :name, :email, roles: []]
@doc """
Creates a new user.
## Examples
iex> TestRepo.Models.User.new("1", "Alice", "alice@example.com")
%TestRepo.Models.User{id: "1", name: "Alice", email: "alice@example.com", roles: []}
"""
def new(id, name, email, roles \\ []) do
%__MODULE__{id: id, name: name, email: email, roles: roles}
end
@doc """
Checks if user has a specific role.
"""
def has_role?(%__MODULE__{roles: roles}, role) do
role in roles
end
@doc """
Adds a role to the user.
"""
def add_role(%__MODULE__{roles: roles} = user, role) do
%{user | roles: [role | roles]}
end
end
defmodule Item do
@type t :: %__MODULE__{
id: String.t(),
name: String.t(),
price: float(),
category: String.t()
}
defstruct [:id, :name, :price, :category]
@doc """
Creates a new item.
## Examples
iex> TestRepo.Models.Item.new("1", "Widget", 19.99, "electronics")
%TestRepo.Models.Item{id: "1", name: "Widget", price: 19.99, category: "electronics"}
"""
def new(id, name, price, category) do
%__MODULE__{id: id, name: name, price: price, category: category}
end
@doc """
Formats price for display.
"""
def display_price(%__MODULE__{price: price}) do
"$#{:erlang.float_to_binary(price, decimals: 2)}"
end
@doc """
Checks if item is in a specific category.
"""
def in_category?(%__MODULE__{category: category}, target_category) do
category == target_category
end
end
defmodule Order do
alias TestRepo.Models.{User, Item}
@type t :: %__MODULE__{
id: String.t(),
user: User.t(),
items: list(Item.t()),
total: float(),
status: atom()
}
defstruct [:id, :user, items: [], total: 0.0, status: :pending]
@doc """
Creates a new order.
"""
def new(id, user, items \\ []) do
total = calculate_total(items)
%__MODULE__{id: id, user: user, items: items, total: total}
end
@doc """
Adds an item to the order.
"""
def add_item(%__MODULE__{items: items} = order, item) do
new_items = [item | items]
%{order | items: new_items, total: calculate_total(new_items)}
end
@doc """
Updates order status.
"""
def update_status(%__MODULE__{} = order, status) do
%{order | status: status}
end
defp calculate_total(items) do
Enum.reduce(items, 0.0, fn item, acc -> acc + item.price end)
end
end
# Protocol implementations
defimpl Serializable, for: User do
def to_map(%User{id: id, name: name, email: email, roles: roles}) do
%{id: id, name: name, email: email, roles: roles}
end
end
defimpl Serializable, for: Item do
def to_map(%Item{id: id, name: name, price: price, category: category}) do
%{id: id, name: name, price: price, category: category}
end
end
defimpl Serializable, for: Order do
def to_map(%Order{id: id, user: user, items: items, total: total, status: status}) do
%{
id: id,
user: Serializable.to_map(user),
items: Enum.map(items, &Serializable.to_map/1),
total: total,
status: status
}
end
end
@doc """
Factory function to create a sample user.
"""
def create_sample_user do
User.new("sample", "Sample User", "sample@example.com", ["user"])
end
@doc """
Factory function to create a sample item.
"""
def create_sample_item do
Item.new("sample", "Sample Item", 9.99, "sample")
end
end
@@ -0,0 +1,257 @@
defmodule TestRepo.Services do
@moduledoc """
Services module demonstrating function usage and dependencies.
Similar to Python's services.py, this module uses the models defined in TestRepo.Models.
"""
alias TestRepo.Models.{User, Item, Order, Serializable}
defmodule UserService do
use GenServer
# Client API
@doc """
Starts the UserService GenServer.
"""
def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, %{}, opts)
end
@doc """
Creates a new user and stores it.
"""
def create_user(pid, id, name, email, roles \\ []) do
GenServer.call(pid, {:create_user, id, name, email, roles})
end
@doc """
Gets a user by ID.
"""
def get_user(pid, id) do
GenServer.call(pid, {:get_user, id})
end
@doc """
Lists all users.
"""
def list_users(pid) do
GenServer.call(pid, :list_users)
end
@doc """
Deletes a user by ID.
"""
def delete_user(pid, id) do
GenServer.call(pid, {:delete_user, id})
end
# Server callbacks
@impl true
def init(_) do
{:ok, %{}}
end
@impl true
def handle_call({:create_user, id, name, email, roles}, _from, users) do
if Map.has_key?(users, id) do
{:reply, {:error, "User with ID #{id} already exists"}, users}
else
user = User.new(id, name, email, roles)
new_users = Map.put(users, id, user)
{:reply, {:ok, user}, new_users}
end
end
@impl true
def handle_call({:get_user, id}, _from, users) do
case Map.get(users, id) do
nil -> {:reply, {:error, :not_found}, users}
user -> {:reply, {:ok, user}, users}
end
end
@impl true
def handle_call(:list_users, _from, users) do
user_list = Map.values(users)
{:reply, user_list, users}
end
@impl true
def handle_call({:delete_user, id}, _from, users) do
if Map.has_key?(users, id) do
new_users = Map.delete(users, id)
{:reply, :ok, new_users}
else
{:reply, {:error, :not_found}, users}
end
end
end
defmodule ItemService do
use Agent
@doc """
Starts the ItemService Agent.
"""
def start_link(opts \\ []) do
Agent.start_link(fn -> %{} end, opts)
end
@doc """
Creates a new item and stores it.
"""
def create_item(pid, id, name, price, category) do
Agent.get_and_update(pid, fn items ->
if Map.has_key?(items, id) do
{{:error, "Item with ID #{id} already exists"}, items}
else
item = Item.new(id, name, price, category)
new_items = Map.put(items, id, item)
{{:ok, item}, new_items}
end
end)
end
@doc """
Gets an item by ID.
"""
def get_item(pid, id) do
Agent.get(pid, fn items ->
case Map.get(items, id) do
nil -> {:error, :not_found}
item -> {:ok, item}
end
end)
end
@doc """
Lists all items, optionally filtered by category.
"""
def list_items(pid, category \\ nil) do
Agent.get(pid, fn items ->
item_list = Map.values(items)
case category do
nil -> item_list
cat -> Enum.filter(item_list, &Item.in_category?(&1, cat))
end
end)
end
@doc """
Deletes an item by ID.
"""
def delete_item(pid, id) do
Agent.get_and_update(pid, fn items ->
if Map.has_key?(items, id) do
new_items = Map.delete(items, id)
{:ok, new_items}
else
{{:error, :not_found}, items}
end
end)
end
end
defmodule OrderService do
@doc """
Creates a new order.
"""
def create_order(id, user, items \\ []) do
Order.new(id, user, items)
end
@doc """
Adds an item to an existing order.
"""
def add_item_to_order(order, item) do
Order.add_item(order, item)
end
@doc """
Updates the status of an order.
"""
def update_order_status(order, status) do
Order.update_status(order, status)
end
@doc """
Processes an order (changes status to :processing).
"""
def process_order(order) do
update_order_status(order, :processing)
end
@doc """
Completes an order (changes status to :completed).
"""
def complete_order(order) do
update_order_status(order, :completed)
end
@doc """
Cancels an order (changes status to :cancelled).
"""
def cancel_order(order) do
update_order_status(order, :cancelled)
end
end
@doc """
Factory function to create a service container.
"""
def create_service_container do
{:ok, user_service} = UserService.start_link()
{:ok, item_service} = ItemService.start_link()
%{
user_service: user_service,
item_service: item_service,
order_service: OrderService
}
end
@doc """
Helper function to serialize any model that implements the Serializable protocol.
"""
def serialize_model(model) do
Serializable.to_map(model)
end
# Module-level variables for testing
@sample_user_id "sample_user"
@sample_item_id "sample_item"
@doc """
Gets the sample user ID.
"""
def sample_user_id, do: @sample_user_id
@doc """
Gets the sample item ID.
"""
def sample_item_id, do: @sample_item_id
# Create some sample data at module load time
def setup_sample_data(container) do
# Create sample user
UserService.create_user(
container.user_service,
@sample_user_id,
"Sample User",
"sample@example.com",
["user", "customer"]
)
# Create sample item
ItemService.create_item(
container.item_service,
@sample_item_id,
"Sample Widget",
29.99,
"electronics"
)
end
end
@@ -0,0 +1,31 @@
defmodule TestRepo do
@moduledoc """
Documentation for `TestRepo`.
"""
@doc """
Hello world.
## Examples
iex> TestRepo.hello()
:world
"""
def hello do
:world
end
@doc """
Adds two numbers together.
## Examples
iex> TestRepo.add(2, 3)
5
"""
def add(a, b) do
a + b
end
end
@@ -0,0 +1,48 @@
defmodule TestRepo.Utils do
@moduledoc """
Utility functions for TestRepo.
"""
@doc """
Converts a string to uppercase.
## Examples
iex> TestRepo.Utils.upcase("hello")
"HELLO"
"""
def upcase(string) when is_binary(string) do
String.upcase(string)
end
@doc """
Calculates the factorial of a number.
## Examples
iex> TestRepo.Utils.factorial(5)
120
"""
def factorial(0), do: 1
def factorial(n) when n > 0 do
n * factorial(n - 1)
end
@doc """
Checks if a number is even.
## Examples
iex> TestRepo.Utils.even?(4)
true
iex> TestRepo.Utils.even?(3)
false
"""
def even?(n) when is_integer(n) do
rem(n, 2) == 0
end
end