Kubernetes-REST

 view release on metacpan or  search on metacpan

t/lib/Test/Kubernetes/Mock.pm  view on Meta::CPAN

    $key =~ s{_+}{_}g;
    $key =~ s{^_}{};
    $self->responses->{$key} = $data;
}

sub call {
    my ($self, $req) = @_;

    # Generate key from method + uri
    my $method = $req->method // 'GET';
    my $path = $req->uri // $req->url // '';
    # Strip host from url if present
    $path =~ s{^https?://[^/]+}{};

    # Strip query parameters for path matching
    my $clean_path = $path;
    $clean_path =~ s{\?.*}{};

    # Check for log lines (one-shot mode, log paths end with /log)
    if (my $lines = $self->log_lines->{$clean_path}) {
        return Test::Kubernetes::Mock::Response->new(
            status => 200,
            content => join("\n", @$lines) . "\n",
        );
    }

    my $key = lc($method) . $path;
    $key =~ s{/}{_}g;
    $key =~ s{_+}{_}g;  # collapse multiple underscores
    $key =~ s{^_}{};

    warn "MOCK: Looking for key '$key' (method=$method, path=$path)\n" if $ENV{MOCK_DEBUG};

    # Check programmatic responses first, then file-based
    my $data = $self->responses->{$key} // Test::Kubernetes::Mock::load_response($key);

    if ($data) {
        return Test::Kubernetes::Mock::Response->new(
            status => 200,
            content => JSON::MaybeXS->new->encode($data),
        );
    }

    # Not found in mock
    return Test::Kubernetes::Mock::Response->new(
        status => 404,
        content => '{"kind":"Status","status":"Failure","message":"not found in mock"}',
    );
}

sub add_watch_events {
    my ($self, $path, $events) = @_;
    $self->watch_events->{$path} = $events;
}

sub add_log_lines {
    my ($self, $path, $lines) = @_;
    $self->log_lines->{$path} = $lines;
}

sub call_streaming {
    my ($self, $req, $callback) = @_;

    my $path = $req->url // '';
    $path =~ s{^https?://[^/]+}{};
    # Strip query parameters for key lookup
    $path =~ s{\?.*}{};

    # Check for log lines first (log paths end with /log)
    if (my $lines = $self->log_lines->{$path}) {
        for my $line (@$lines) {
            $callback->($line . "\n", undef);
        }
        return Test::Kubernetes::Mock::Response->new(
            status => 200,
        );
    }

    # Check for watch events
    my $events = $self->watch_events->{$path};

    unless ($events) {
        return Test::Kubernetes::Mock::Response->new(
            status => 404,
            content => '{"kind":"Status","status":"Failure","message":"no streaming data for path"}',
        );
    }

    my $json = JSON::MaybeXS->new;
    for my $event (@$events) {
        my $line = $json->encode($event) . "\n";
        $callback->($line, undef);
    }

    return Test::Kubernetes::Mock::Response->new(
        status => 200,
    );
}

package Test::Kubernetes::Mock::Response;
use Moo;

has status => (is => 'ro', required => 1);
has content => (is => 'ro', default => sub { '' });

1;

__END__

=head1 NAME

Test::Kubernetes::Mock - Mock HTTP layer for Kubernetes::REST tests

=head1 SYNOPSIS

    use Test::Kubernetes::Mock qw(mock_api live_api is_live);

    my $api;
    if (is_live()) {
        $api = live_api();
    } else {
        $api = mock_api();
    }

    my $pods = $api->list('Pod');

=head1 DESCRIPTION

Provides mock and live API instances for testing Kubernetes::REST.

=head1 ENVIRONMENT VARIABLES

=over 4

=item TEST_KUBERNETES_REST_KUBECONFIG

Path to kubeconfig file for live tests. B<Required> for live tests.
This is intentionally a long name to prevent accidentally running
tests against production clusters.

    TEST_KUBERNETES_REST_KUBECONFIG=~/.kube/test-config prove -l t/

=item TEST_KUBERNETES_REST_CONTEXT

Optional. Kubernetes context to use from the kubeconfig.



( run in 1.033 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )