TestMethodSink.java

package org.egothor.methodatlas.emit;

import java.util.List;

import org.egothor.methodatlas.ai.AiMethodSuggestion;

/**
 * Consumer that receives a single discovered test method record.
 *
 * <p>
 * This functional interface decouples the orchestration layer from the output
 * format. During a scan run, one implementation is created up-front and every
 * discovered test method is forwarded to it via {@link #record}.
 * </p>
 *
 * @see org.egothor.methodatlas.emit.OutputEmitter
 * @see org.egothor.methodatlas.emit.SarifEmitter
 */
@FunctionalInterface
public interface TestMethodSink {

    /**
     * Records a single test method.
     *
     * @param fqcn        fully qualified name of the class that declares the
     *                    method
     * @param method      simple method name
     * @param beginLine   one-based line number of the first line of the method
     *                    declaration; {@code 0} when the parser cannot determine
     *                    the location
     * @param loc         inclusive line count of the method declaration
     * @param contentHash lowercase-hex SHA-256 fingerprint of the enclosing
     *                    class source, or {@code null} when
     *                    {@code -content-hash} is not enabled
     * @param tags        source-level test-framework tag values declared on the
     *                    method; never {@code null}
     * @param displayName text from an existing display-name annotation on the
     *                    method; {@code null} when no such annotation is present;
     *                    {@code ""} when the annotation is present but has an
     *                    empty value — which is a malformed annotation and may
     *                    be treated as a quality finding by implementations
     * @param suggestion  AI-generated security classification for the method,
     *                    or {@code null} when AI enrichment is disabled or
     *                    unavailable for this class
     */
    @SuppressWarnings("PMD.UseObjectForClearerAPI")
    void record(String fqcn, String method, int beginLine, int loc, String contentHash,
            List<String> tags, String displayName, AiMethodSuggestion suggestion);

    /**
     * Records a single test method together with the real path of the source file
     * that declares it.
     *
     * <p>
     * Sinks that emit file locations (SARIF, GitHub annotations) override this to
     * use {@code sourcePath} instead of synthesising a Java-style path from the
     * FQCN, which is wrong for every non-Java language. The default implementation
     * ignores the path and delegates to
     * {@link #record(String, String, int, int, String, List, String, AiMethodSuggestion)}.
     * </p>
     *
     * @param fqcn        fully qualified name of the declaring class
     * @param method      simple method name
     * @param beginLine   one-based first line of the method, or {@code 0}
     * @param loc         inclusive line count of the method declaration
     * @param contentHash class content fingerprint, or {@code null}
     * @param tags        source-level tag values; never {@code null}
     * @param displayName existing display-name text, or {@code null}
     * @param suggestion  AI classification, or {@code null}
     * @param sourcePath  forward-slash path of the source file relative to the scan
     *                    root, or {@code null} when unknown
     */
    @SuppressWarnings("PMD.UseObjectForClearerAPI")
    default void recordWithSourcePath(String fqcn, String method, int beginLine, int loc, String contentHash,
            List<String> tags, String displayName, AiMethodSuggestion suggestion, String sourcePath) {
        record(fqcn, method, beginLine, loc, contentHash, tags, displayName, suggestion);
    }
}