Wie kann ich inet_client_addr () für Unit-Tests in PostgreSQL fälschen?

7

Ich habe eine einfache gespeicherte Prozedur, deren Rückgabewert vom Wert von abhängt inet_client_addr(). Wie kann ich überschreibeninet_client_addr() beim Testen meiner gespeicherten Prozedur zum Zwecke von Komponententests ?

Die einzige Lösung, die ich bisher gefunden habe, besteht darin, eine Wrapper-Funktion zu erstellen inet_client_addr():

CREATE FUNCTION my_inet_client_addr() RETURNS INET AS $$
    SELECT inet_client_addr();
$$ LANGUAGE sql;

Dann benutze das in meiner Funktion:

CREATE local_connection() RETURNS BOOLEAN AS $$
    SELECT my_inet_client_addr() = '127.0.0.1';
$$ LANGUAGE sql;

Dann kann ich in meinem Unit-Test Folgendes neu definieren my_inet_client_addr():

BEGIN;
SELECT PLAN(2);
REPLACE FUNCTION my_inet_client_addr() RETURNS INET AS $$
    SELECT '127.0.0.1'::INET;
$$ LANGUAGE sql;

is(local_connection(),TRUE,'Connection from 127.0.0.1 is local');

REPLACE FUNCTION my_inet_client_addr() RETURNS INET AS $$
    SELECT '192.168.1.1'::INET;
$$ LANGUAGE sql;

is(local_connection(),FALSE,'Connection from 192.168.1.1. is not local');

ROLLBACK;

Gibt es eine Möglichkeit, dies auch ohne die Wrapper-Funktion zu erreichen my_inet_client_addr()?

Schwach
quelle
die Abhängigkeit injizieren? your_procedure( inet_client_addr() )Anstelle eines impliziten Aufrufs ist kein Verspotten / Überschreiben erforderlich.
Xenoterracide
@xenoterracide: Das könnte funktionieren ... obwohl es sicherlich ärgerlich wäre und viel Code berühren würde.
Flimzy
Ich vergesse, ob in pgsql, ob die Signatur sehr wichtig ist oder nicht, aber Sie können sie auch als optionalen Parameter verwenden. Wenn sie übergeben wird, wird der übergebene Wert verwendet, wenn nicht inet_client_addr(), oder wenn Signaturen sehr wichtig sind, haben Sie das Sig delegieren Sie ohne den zusätzlichen Parameter an den mit. In beiden Fällen bleibt die vorhandene API erhalten, während die Kernlogik getestet werden kann.
Xenoterracide
@xenoterracide: Signaturen sind wichtig, aber ich könnte dann eine local_connection(INET)Funktion erstellen (die das Argument übernimmt), die wiederum von einer local_connection()Funktion umschlossen wird , die übergeben wird inet_client_addr(). Dies ist wahrscheinlich eine sauberere Methode, bei der die vorhandene API erhalten bleibt.
Flimzy

Antworten:

7

inet_client_addr()ist eine Systeminformationsfunktion.
Es befindet sich pg_catalogwie andere integrierte Funktionen im Schema (mit Ausnahme zusätzlicher Module).

pg_catalogist automatisch Teil der search_path. Pro Dokumentation:

Zusätzlich zu publicund vom Benutzer erstellten Schemas enthält jede Datenbank ein pg_catalogSchema, das die Systemtabellen und alle integrierten Datentypen, Funktionen und Operatoren enthält. pg_catalogist immer effektiv Teil des Suchpfads. Wenn es im Pfad nicht explizit benannt ist, wird es implizit durchsucht, bevor die Schemas des Pfads durchsucht werden. Dies stellt sicher, dass integrierte Namen immer auffindbar sind. Allerdings können Sie explizit setzen pg_catalogam Ende des Suchpfades , wenn Sie es vorziehen , benutzerdefinierte Namen überschreiben eingebaute Namen zu haben.

Meine kühne Betonung.
Also erstellen wir ein dediziertes Schema und platzieren es vorher pg_catalog in search_path:

CREATE SCHEMA override;

CREATE OR REPLACE FUNCTION override.inet_client_addr()
  RETURNS inet AS
$func$
SELECT '127.0.0.1'::inet
$func$ language sql STABLE;
SET search_path = override, pg_catalog, public;

Dann findet Ihr Anruf zuerst Ihre benutzerdefinierte Überschreibungsfunktion:

SELECT inet_client_addr();

SQL Fiddle

Stellen Sie sicher, dass nicht privilegierte Benutzer keine Objekte im overrideSchema erstellen oder Ihnen alle möglichen Streiche spielen können. Dies ist standardmäßig nicht der Fall. Pro Dokumentation:

PUBLIC werden standardmäßig keine Berechtigungen für Tabellen, Spalten, Schemas oder Tabellenbereiche gewährt .

Meine kühne Betonung.

Vorsicht ist geboten, wenn derselbe Benutzer Objekte in der Datenbank erstellen darf .
Pro Dokumentation:

Das erste im Suchpfad genannte Schema wird als aktuelles Schema bezeichnet. Abgesehen davon, dass das erste Schema durchsucht wird, ist es auch das Schema, in dem neue Tabellen erstellt werden, wenn der CREATE TABLEBefehl keinen Schemanamen angibt.

CREATEGeben Sie immer einen Schemanamen für Befehle an und lassen Sie das Kistenobjekt standardmäßig nicht für alle zu, um Fehler auszuschließen.

Erwin Brandstetter
quelle
1
verdammt, ich habe das versucht, aber ich habe custom, public anstelle von custom, pg_catalog, public verwendet und es hat nicht funktioniert :) Gute Antwort.
Neil McGuigan
@NeilMcGuigan: Du warst auf dem richtigen Weg. +1 für deine Antwort.
Erwin Brandstetter
Ich habe auch Neils Ansatz ausprobiert ... die explizite Angabe pg_catalogim Suchpfad war das fehlende Teil. Vielen Dank!
Flimzy
3

Muss postgresBenutzer oder andere Superuserfür meine sein. Ich brauche nur create schemaPrivilegien für Erwins Antwort.

Tests starten:

alter function inet_client_addr() rename to _inet_client_addr;

create function inet_client_addr() returns inet language sql stable cost 1 as '
  select ''192.168.0.1''::inet;
';

select inet_client_addr();

Tests beenden

alter function inet_client_addr() rename to inet_client_addr_mock;

alter function _inet_client_adds() rename to inet_client_addr;

--the regular function:
select inet_client_addr();
Neil McGuigan
quelle
+1 Keine schlechte Lösung, aber ich habe Erins Antwort akzeptiert, da ich denke, dass sie etwas robuster ist.
Flimzy